Dokumentation für das Modul PageTree[Ansicht] [Bearbeiten] [Versionsgeschichte] [Aktualisieren]

Funktion

Das Modul PageTree stellt eine Liste von Artikel in einer gewünschten hierarchischen Struktur dar. Die entsprechenden Artikel sind dabei in einem Untermodul zu deklarieren.

Verwendung

Vorlagen, die dieses Modul benutzen

  • {{Hilfe}} – Navigationsbox für alle Hilfeartikel auf Wikivoyage.

Vorhandene Artikellisten

Hinweise
local PageTree = { suite   = "PageTree",                    serial  = "2018-09-13",                    item    = 56033297,                    maxSub  = 10,                    strings = { "segment",                                "self",                                "stamped",                                "subpager",                                "suppress" },                    toggles = { "lazy",                                "level",                                "lineup",                                "light",                                "linked",                                "limit",                                "list" } } --[=[ Module:PageTree Rendering and administration of hierarchical wiki page structures ]=]    local function face( about )     -- Ensure presence of entry title     --     about  -- table, with entry     --               .show   -- link title     --               .seed   -- page name     if not about.show then        about.show = about.seed:match( "/([^/]+)$" )        if not about.show then            about.show = about.seed:match( "^[^:]+:(.+)$" )            if not about.show then                about.show = about.seed            end        end     end end -- face()    local function facility( access )     -- Load data table     --     access  -- string, with path of module     --                        maybe relative, if starting with "/"     local s = access     local lucky, r     if s:byte( 1, 1 ) == 47 then    -- "/"         if PageTree.suite then             s = PageTree.suite .. s         end     end     lucky, r = pcall( mw.loadData, s )     if type( r ) ~= "table" then         r = string.format( "'%s' invalid",  s )     end     return r end -- facility()    local function factory( apply )     -- Clone read-only table     --     apply  -- table, with basic data elements, read-only     -- Returns message with markup     local r = { }     for k, v in pairs( apply ) do         r[ k ] = v     end -- for k, v     return r end -- factory()    local function fade( ask )     -- Check whether page is to be hidden     --     ask  -- string, with page name     -- Returns true if to be hidden     local r = false     for k, v in pairs( PageTree.hide ) do         if ask:match( v ) then             r = true             break -- for k, v         end     end -- for k, v     return r end -- fade()    local function failures()     -- Check all pages     local redirect = {}     local unknown  = {}     local r, s, title     local n = 0     for k, v in pairs( PageTree.pages ) do         n = n + 1         s = v.seed         if type( s ) == "string" then             title = mw.title.new( s )             if not title then                 table.insert( unknown, s )             elseif title.exists then                 if v.shift then                     if not title.isRedirect then                         table.insert( redirect,                                       "(-)" .. s )                     end                 elseif PageTree.linked and                        title.isRedirect then                     table.insert( redirect,                                   "(+)" .. s )                 end             else                 table.insert( unknown, s )             end         end     end -- for k, v     r = string.format( "n=%d", n )     n = table.maxn( unknown )     if n > 0 then         s = "*** unknown:"         for i = 1, n do             r = string.format( "%s %s %s", r, s, unknown[ i ] )             s = "|"         end -- for i     else         n = table.maxn( redirect )         if n > 0 then             s = "*** unexpected redirect:"             for i = 1, n do                 r = string.format( "%s %s %s", r, s, redirect[ i ] )                 s = "|"             end -- for i         end     end     return r end -- failures()    local function fair( adopt )     -- Expand relative page name, if necessary     --     adopt  -- string, with page name     -- Returns absolute page name, or false     local r     if adopt:byte( 1, 1 ) == 47 then    -- "/"         r = PageTree.start .. adopt:sub( 2 )     else         r = adopt     end     r = mw.text.trim( r )     if r == "" then         r = false     end     return r end -- fair()    local function fasten( adopt )     -- Format restrictions     --     adopt  -- string, with restriction entry     -- Returns absolute page name, or false     local designs = {      autoconfirmed       = "background:#FFFF80",      editeditorprotected = "background:#FFFF00;border:#FF0000 1px solid",      superprotect        = "background:#FF0000;border:#FFFF00 9px solid",      sysop               = "background:#FFFF00;border:#FF0000 3px solid",      ["?????????"]       = "border:#FF0000 5px solid;color:#FF0000" }     local restrictions = mw.text.split( adopt, ":" )     local r = ""     local start = "margin-left:2em;"     local staff, strict, style     for i = 1, #restrictions do         strict, staff = restrictions[ i ]:match( "^(.*)=(.+)$" )         strict = mw.text.trim( strict )         if strict == "" then             strict = "?????????"         end         style = designs[ staff ]         if not style then             style  = designs[ "?????????" ]             strict = strict .. "?????????"         end         if start then             style = start .. style             start = false         end         style = style .. ";padding-left:3px;padding-right:3px;"         r     = string.format( "%s<span style='%s'>%s</span>",                            r, style, strict )     end -- for i     return r end -- fasten()    local function fatal( alert )     -- Format disaster message with class="error" and put into category     --     alert   -- string, with message, or other data     -- Returns message string with markup     local ecat = mw.message.new( "Scribunto-common-error-category" )     local r = type( alert )     if r == "string" then         r = alert     else         r = "???? " .. r     end     if ecat:isBlank() then         ecat = ""     else         ecat = string.format( "[[Category:%s]]", ecat:plain() )     end     r = string.format( "<span class=\"error\">FATAL LUA ERROR %s</span>",                        r )         .. ecat     return r end -- fatal()    local function father( ancestor )     -- Find parent page     --     ancestor  -- string, with page name     -- Returns page name of parent, or PageTree.series     local r = ancestor:match( "^(.+)/[^/]+$" )     if not r then         r = ancestor:match( "^([^:]+:).+$" )         if not r then             r = PageTree.series         end     end     return r end -- father()    local function fault( alert )     -- Format message with class="error"     --     alert  -- string, with message     -- Returns message with markup     return string.format( "<span class=\"error\">%s</span>", alert ) end -- fault()    local function features( apply, access )     -- Fill PageTree.pages with elements     --     apply   -- table, with definitions, read-only     --     access  -- string, with relative path of module     -- Returns error message, if failed, or false, if fine     local r, e, s     local bad = { }     local tmp = { }     for k, v in pairs( apply ) do         s = type( k )         e = false         if s == "number" then             s = type( v )             if s == "string" then                 s = v                 e = { }             elseif s == "table" then                 if type( v.seed ) == "string" then                     s = v.seed                     e = factory( v )                 end             end         elseif s == "string" then             if type( v ) == "table" then                 s = k                 e = factory( v )             end         elseif k == true then    -- root             if PageTree.pages[ true ] then                 bad[ "true" ] = "duplicated"             elseif type( v ) == "table" then                 if type( v.seed ) == "string" then                     PageTree.pages[ true ]          = factory( v )                     PageTree.pages[ true ].children = { }                 else                     bad[ "true" ] = "seed missing"                 end             else                 bad[ "true" ] = "invalid"             end         end         if e then             s = fair( s )             if tmp[ s ] then                 bad[ s ] = "duplicated"             else                 tmp[ s ] = true             end             if s then                 if not PageTree.pages[ s ] then                     e.seed = s                     if e.super then                         if type( e.super ) == "string" then                             e.super = fair( e.super )                         end                     elseif e.super == nil then                         e.super = father( s )                     end                     e.children = { }                     PageTree.pages[ s ] = e                 end             end         end     end -- for k, v     e = 0     r = string.format( " in '%s'", access )     for k, v in pairs( bad ) do         e = e + 1         r = string.format( "%s * [%s]: %s ", r, k, v )     end -- for k, v     if e == 0 then         r = false     elseif e == 1 then         r = "Error" .. r     else         r = "Errors" .. r     end     return r end -- features()    local function feed( access )     -- Fill PageTree with data, if not yet set     --     access  -- string, with relative path of module     -- Returns error message, if failed, or false, if fine     local r = facility( access )     if type( r ) == "table" then         local s         if type( r.maxSub ) == "number" then             PageTree.maxSub = r.maxSub         end         if type( r.stamp ) == "string" then             if PageTree.stamp then                 if PageTree.stamp < r.stamp then                     PageTree.stamp = r.stamp                 end             else                 PageTree.stamp = r.stamp             end         end         if type( r.start ) == "string" then             s = mw.text.trim( r.start )             if s ~= "" then                 PageTree.start = s             end         end         if not PageTree.pages then             PageTree.pages = { }         end         if type( r.pages ) == "table" then             if not PageTree.pages then                 PageTree.pages = { }             end             s = features( r.pages, access )             if s then                 r = s             end         end         if type( r ) == "table" then             if type( r.sub ) == "string" then                 r = feed( string.format( "%s/%s", access, r.sub ) )             else                 r = false             end         end     end     return r end -- feed()    local function field( about, absolute )     -- Format entry as link     --     about     -- table, with entry     --                  .show        -- link title     --                  .seed        -- page name     --                  .shift       -- redirect target     --                  .protection  -- restrictions     --     absolute  -- true, if real page name to be shown     -- Returns string     local r     if absolute then         r = string.format( "[[%s]]", about.seed )     else         face( about )         if about.show == about.seed then             r = string.format( "[[%s]]", about.seed )         else             r = string.format( "[[%s|%s]]", about.seed, about.show )         end     end     if type( about.suffix ) == "string" then         r = string.format( "%s %s", r, about.suffix )     end     if PageTree.linked and type( about.shift ) == "string" then         r = string.format( "%s <small>&#8594;[[%s]]</small>",                            r, fair( about.shift ) )     end     if PageTree.limit and type( about.protection ) == "string" then         r = string.format( "%s %s",                            r, fasten( about.protection ) )     end     return r end -- field()    local function filter( adjust )     -- Create sort key (Latin ASCII upcased)     --     adjust  -- string, to be standardized     -- Returns string with key     if not PageTree.Sort then         r, PageTree.Sort = pcall( require, "Module:Sort" )         if type( PageTree.Sort ) == "table" then             PageTree.Sort = PageTree.Sort.Sort()         else             error( "Module:Sort not ready" )         end     end     return string.upper( PageTree.Sort.lex( adjust, "latin", false ) ) end -- filter()    local function first( a1, a2, abs )     -- Compare a1 with a2 in lexicographical order     --     a1   -- table, with page entry     --     a2   -- table, with page entry     --     abs  -- true, if .show to be used rather than .seed     -- Returns true if a1 < a2     if not a1.sort then         if abs then             face( a1 )             a1.sort = filter( a1.show )         else             a1.sort = filter( a1.seed )         end     end     if not a2.sort then         if abs then             face( a2 )             a2.sort = filter( a2.show )         else             a2.sort = filter( a2.seed )         end     end     return ( a1.sort < a2.sort ) end -- first()    local function firsthand( a1, a2 )     -- Compare a1 with a2, considering .show     --     a1  -- string, with page name     --     a2  -- string, with page name     -- Returns true if a1 < a2     return first( a1, a2, true ) end -- first()    local function firstly( a1, a2 )     -- Compare a1 with a2, considering .index     --     a1  -- string, with page name     --     a2  -- string, with page name     -- Returns true if a1 < a2     local e1 = PageTree.pages[ a1 ]     local e2 = PageTree.pages[ a2 ]     local r     if e1.index then         if e2.index then             r = ( e1.index < e2.index )         else             r = true         end     elseif e2.index then         r = false     else         r = first( e1, e2, true )     end     return r end -- firstly()    local function flag( ahead )     -- Returns string with leading list syntax, either "#" or "*" or ":"     --     ahead  -- string, with syntax in case of .lazy     local r     if PageTree.lazy then         r = ":"     else         r = ahead     end     return r end -- flag()    local function flip( already, ahead, amount, above )     -- Render subtree as expandable/collapsible list of entries     --     already  -- number, of initially visible levels     --     ahead    -- string, leading list syntax, either "#" or "*"     --     amount   -- number, of leading elements     --     above    -- table, with top element (not shown)     --                 .children -- will be shown     -- Returns string with story     local n = table.maxn( above.children )     local r = ""     if n > 0 then         local live = ( already <= amount ) --      local span = "<span ></span>"         local e, let, serial         table.sort( above.children, firstly )         for i = 1, n do             e = PageTree.pages[ above.children[ i ] ]             if e.list == false then                 let = PageTree.list             elseif PageTree.hide then                 let = not fade( e.seed )             else                 let = true             end             if let then                 local s                 if not e.less then                     PageTree.item = PageTree.item + 1                     serial        = string.format( "%s_%d",                                                    PageTree.serial,                                                    PageTree.item )                     if table.maxn( e.children ) > 0 then                         s = "mw-collapsible"                         if amount >= already then                             s = s .. " mw-collapsed"                         end                         r = string.format( "%s\n<div class='%s' %s %s>",                                               r,                                               s,                                               "data-expandtext='[+]'",                                               "data-collapsetext='[-]'" )                         s = "</div>"                     else                         s = ""                     end                 end                 r = string.format( "%s\n%s%s",                                    r,                                    string.rep( ahead, amount ),                                    field( e, false ) )                 if not e.less then                     local style                     if amount >= already then                         style  = " style='display:none'"                     else                         style = ""                     end                     r = string.format( "%s\n<div %s%s>\n%s\n</div>%s",                                        r, --                                     span,                                        "class='mw-collapsible-content'",                                        style,                                        flip( already,                                              ahead,                                              amount + 1,                                              e ),                                        s )                 end             end         end -- for i     end     return r end -- flip()    local function flow( acquire )     -- Collect the .super in path     --     acquire  -- string, with page name     if type( acquire ) == "string" then         local e = PageTree.pages[ acquire ]         local s = false         if e then             s = e.super         end         if not s then             s = acquire:match( "^(.+)/[^/]+$" )             if not s then                 s = acquire:match( "^([^:]+:)" )             end             if s then                 if not e then                     e                        = { children = { },                                                  seed     = acquire }                     PageTree.pages[ acquire ] = e                 end                 e.super = s             elseif e then                 e.super = true             end         end         if type( s ) == "string"  and  s~= acquire then             flow( s )         end     end end -- flow()    local function fluent()     -- Collect all .children; add .super where missing     local let = true     local e     for k, v in pairs( PageTree.pages ) do         if v.super == nil then             flow( k )         elseif not PageTree.pages[ v.super ] then             flow( v.super )         end     end -- for k, v     for k, v in pairs( PageTree.pages ) do         if PageTree.level then             let = ( not v.seed:find( "/" ) )         end         if let and v.super then             e = PageTree.pages[ v.super ]             if e then                 table.insert( e.children, k )             end         end     end -- for k, v end -- fluent()    local function follow( ahead, amount, above, all )     -- Render subtree as list of entries     --     ahead   -- string, with leading list syntax, either "#" or "*"     --     amount  -- number, of leading elements     --     above   -- table, with top element (not shown)     --                .children -- will be shown     --     all     -- true if all grandchildren shall be shown     -- Returns string with story     local n = table.maxn( above.children )     local r = ""     if n > 0 then         local e, let, lift         local start = "\n" .. string.rep( ahead, amount )         table.sort( above.children, firstly )         for i = 1, n do             e    = PageTree.pages[ above.children[ i ] ]             lift = ( all or above.long )             if e.list == false then                 let = PageTree.list             elseif PageTree.hide then                 let = not fade( e.seed )             else                 let = lift             end             if let then                 r = string.format( "%s%s%s",                                    r,  start,  field( e, false ) )                 if lift and ( all or not e.less ) then                     r = r .. follow( ahead,  amount + 1,  e,  all )                 end             end         end -- for i     end     return r end -- follow()    local function formatAll()     -- Render as single list of entries     local collect = { }     local n       = 0     local r, let     for k, v in pairs( PageTree.pages ) do         let = true         if v.list == false  and            ( not PageTree.list or v.loose or k == true ) then             let = false         elseif PageTree.level and v.seed:find( "/" ) then             let = false         elseif PageTree.hide then             let = not fade( v.seed )         end         if let then             if v.show then                 v.show = nil             end             if PageTree.light then                 local j, k = v.seed:find( PageTree.start )                 if j == 1 then                     v.show = v.seed:sub( k + 1 )                 end             end             n            = n + 1             collect[ n ] = v         end     end -- for k, v     if n > 0 then         local start         local long = ( not PageTree.light )         if PageTree.lineup then             start = " * "         else             start = "\n" .. flag( "#" )         end         table.sort( collect, firsthand )         r = ""         for k, v in pairs( collect ) do             r = string.format( "%s%s%s",                                r,                                start,                                field( v, long ) )         end -- for k, v     else         r = false     end     return r end -- formatAll()    local function formatExpand( ancestor, args )     -- Render entire tree as collapsible list text     --     ancestor  -- string, with name of root element, or false     --     args      -- table, with control information     -- Returns string with story, or false     local init, r     if type( ancestor ) == "string" then         r = ancestor     else         r = true     end     r = PageTree.pages[ r ]     if r then         if type( PageTree.init ) == "number" then             init = PageTree.init             if PageTree.init < 1 then                 init = 1             end         else             init = 1         end         if type( PageTree.serial ) ~= "string"            or PageTree.serial == "" then             PageTree.serial = "pageTree"         end         PageTree.item = 0         r = flip( init,  flag( "*" ),  1,  r )     else         r = false     end     return r end -- formatExpand()    local function formatPath( ancestor )     -- Render tree as partially opened list     --     ancestor  -- string, with name of root element, or false     -- Returns string with story     local sup = PageTree.self     local higher, i, r     if ancestor then         higher = PageTree.pages[ ancestor ]         if type( higher ) == "table" then             higher.super = false         end     else         local point = PageTree.pages[ sup ]         if not point then             sup = true         elseif point.list == false then             higher = PageTree.pages[ sup ]             if type( higher ) == "table" then                 if not higher.loose then                     sup = true                 end             else                 sup = true             end         end     end     for i = PageTree.maxSub, 0, -1 do         higher = PageTree.pages[ sup ]         if type( higher ) == "table" then             higher.long = true             sup         = higher.super             if not sup then                 break    -- for             end         else             higher = false             break    -- for         end     end    -- for --i     if higher then         r = follow( flag( "*" ),  1,  higher,  false )     else         r = false     end     return r end -- formatPath()    local function formatSub( amend, around )     -- Render tree as subpage hierarchy sequence     --     amend   -- string, with name of template, or false     --     around  -- object, with frame, or false     -- Returns string with story, or false     local higher     local n       = 1     local reverse = { }     local sup     = PageTree.self     local r     if type( sup ) == "string" and not sup:find( "/", 1, true ) then         flow( sup )         repeat             higher = PageTree.pages[ sup ]             if type( higher ) == "table" then                 sup          = higher.super                 reverse[ n ] = higher                 if higher.loose then                     n = -1                     break    -- repeat                 elseif sup then                     n = n + 1                     if n > PageTree.maxSub then                         reverse[ n ] = { seed = "???????" }                         break    -- repeat                     end                 else                     break    -- repeat                 end             else                 break    -- repeat             end         until not higher     end     if n > 1 then         for i = n, 2, -1 do             reverse[ i ] = field( reverse[ i ], false )         end -- for i         if amend then             local frame             local ordered = { }             if around then                 frame = around             else                 frame = mw.getCurrentFrame()             end             for i = n, 2, -1 do                 ordered[ n - i + 1 ] = reverse[ i ]             end -- for i             r = frame:expandTemplate{ title=amend, args=ordered }         else             r = ""             for i = n, 2, -1 do                 if i < n then                     r = r .. "&#160;&#62; "                 end                 r = r .. reverse[ i ]             end -- for i         end     else         r = false     end     return r end -- formatSub()    local function formatTree( ancestor )     -- Render entire tree as list text     --     ancestor  -- string, with name of root element, or false     -- Returns string with story, or false     local r     if type( ancestor ) == "string" then         r = ancestor     else         r = true     end     r = PageTree.pages[ r ]     if r then         r = follow( flag( "#" ),  1,  r,  true )     else         r = false     end     return r end -- formatTree()    local function forward( args )     -- Execute main task     --     args  -- table, with arguments     -- Returns string with story, or false     local r     if type( args.series ) == "string"  and        type( args.service ) == "string"  and        type( args.suite ) == "string" then         PageTree.series  = args.series         PageTree.service = args.service         PageTree.suite   = args.suite         if type( args.hide ) == "table" then             PageTree.hide = args.hide         elseif type( args.suppress ) == "string" then             PageTree.hide = { }             table.insert( PageTree.hide, args.suppress )         end         if PageTree.series:match( "[:/]$" ) then             PageTree.start = args.series         else             PageTree.start = args.series .. "/"         end         r = feed( "/" .. PageTree.series )         if r then             r = fault( r )         else             local life = true             if PageTree.service == "path"  or                PageTree.service == "subpages" then                 if args.self then                     PageTree.self = args.self                 else                     PageTree.page = mw.title.getCurrentTitle()                     PageTree.self = PageTree.page.prefixedText                 end                 if not PageTree.pages[ PageTree.self ] then                      if type( PageTree.pages[ true ] ) == "table" then                         PageTree.self = true                     else                         life = false                     end                 end             end             if life then                 if PageTree.service == "subpages" then                     r = formatSub( args.subpager, args.frame )                 elseif PageTree.service == "check" then                     PageTree.linked = args.linked                     r = failures()                 else                     for k, v in pairs( PageTree.toggles ) do                         PageTree[ v ] = args[ v ]                     end -- for k, v                     if PageTree.service == "all" then                         r = formatAll()                     else                         local segment                         if type( args.segment ) == "string" then                             segment = fair( args.segment )                             if not PageTree.pages[ segment ] then                                 PageTree.pages[ segment ] =                                                     { seed     = segment,                                                       children = { },                                                       super    = true,                                                       list     = false }                             end                         end                         fluent()                         if PageTree.service == "path" then                             r = formatPath( segment )                         elseif PageTree.service == "expand" then                             r = formatExpand( segment, args )                         else                             if args.limit == "1"  or                                args.limit == true then                                 PageTree.limit = true                             end                             r = formatTree( segment )                         end                     end                     if r and args.stamped and PageTree.stamp then                         local babel = mw.language.getContentLanguage()                         local stamp = babel:formatDate( args.stamped,                                                         PageTree.stamp )                         r = stamp .. r                     end                 end             else                 r = false             end         end     end     return r end -- forward()    local function framed( frame, action )     -- #invoke call     --     action  -- string, with keyword     local params = { service = action,                      suite   = frame:getTitle() }     local pars   = frame.args     local r      = pars[ 1 ]     if r then         params.series = mw.text.trim( r )         if params.series == "" then             r = false         end     end     if r then         local lucky         params.frame = frame         for k, v in pairs( PageTree.strings ) do             if pars[ v ]  and  pars[ v ] ~= "" then                 params[ v ] = pars[ v ]             end         end -- for k, v         for k, v in pairs( PageTree.toggles ) do             if pars[ v ] then                 params[ v ] = ( pars[ v ] == "1" )             end         end -- for k, v         lucky, r = pcall( forward, params )         if not lucky then             r = fatal( r )         end     else         r = fault( "'1=' missing" )     end     if not r then         r = ""     end     return r end -- framed()    PageTree.failsafe = function ( assert )     -- Retrieve versioning and check for compliance     -- Precondition:     --     assert  -- string, with required version or "wikidata",     --                or false     -- Postcondition:     --     Returns  string with appropriate version, or false     local r     local since = assert     if since == "wikidata" then         local item = PageTree.item         since = false         if type( item ) == "number"  and  item > 0 then             local ent = mw.wikibase.getEntity( string.format( "Q%d",                                                               item ) )             if type( ent ) == "table" then                 local vsn = ent:formatPropertyValues( "P348" )                 if type( vsn ) == "table"  and                    type( vsn.value ) == "string"  and                    vsn.value ~= "" then                     r = vsn.value                 end             end         end     end     if not r then         if not since  or  since <= PageTree.serial then             r = PageTree.serial         else             r = false         end     end     return r end -- PageTree.failsafe()    -- Export local p = { }  -- lazy   = do not number but use bullets or nothing -- level  = top level entries only -- light  = strip prefix -- linked = show redirects -- list   = show suppressed entries  function p.all( frame )     return  framed( frame, "all" ) end -- p.all  function p.check( frame )     return  framed( frame, "check" ) end -- p.check  function p.expand( frame )     return  framed( frame, "expand" ) end -- p.expand  function p.path( frame )     return  framed( frame, "path" ) end -- p.path  function p.subpages( frame )     return  framed( frame, "subpages" ) end -- p.subpages  function p.tree( frame )     return  framed( frame, "tree" ) end -- p.tree  function p.test( args )     -- Debugging     --     args  -- table, with arguments; mandatory:     --              .series   -- tree     --              .service  -- action mode     --              .suite    -- Module path     --              .self     -- page name, in service="path"     --              .limit    -- show restrictions     local lucky, r = pcall( forward, args )     return r or PageTree end -- p.test()  p.failsafe = function ( frame )     -- Check or retrieve version information     -- Precondition:     --     frame  -- object; #invoke environment     -- Postcondition:     --     Return string with error message or ""     -- Uses:     --     PageTree.failsafe()     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 PageTree.failsafe( since )  or  "" end -- p.failsafe()  return p