Modul:PageTree
Erscheinungsbild
Dokumentation für das Modul PageTree[Ansicht] [Bearbeiten] [Versionsgeschichte] [ ]
Dieses Modul wurde am 13. September 2018 von Modul:PageTree der deutschen Wikipedia importiert. Statt Änderungen hier auf Wikivoyage vorzunehmen, sollte eine neuer Import vorgezogen werden, falls im originalen Wiki neue Funktionen hinzugekommen sind. Stimme dich dazu bitte mit der Community in der Vorlagenwerkstatt ab. |
Das Modul sollte aktualisiert werden! |
Dieses Modul ist getestet und für den projektweiten Gebrauch geeignet. Es kann in Vorlagen benutzt und auf Hilfeseiten erläutert werden. Entwicklungen an dem Modul sollten auf PageTree/Test und die Anwendung auf der Spielwiese getestet werden, da wiederholte Trial-and-Error-Edits die Resourcen stark belasten können. |
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
- Wikipedia:Lua/Modul/PageTree/de – Ausführliche Dokumentation des Moduls
Vorlagen, die dieses Modul benutzen
- {{Hilfe}} – Navigationsbox für alle Hilfeartikel auf Wikivoyage.
Vorhandene Artikellisten
- Module:PageTree Submodule – Die Kategorie enthält alle derzeit verfügbaren Submodule mit Artikellisten.
Hinweise
- Die obige Dokumentation wurde aus der Seite Modul:PageTree/Doku eingefügt. (bearbeiten | Versionsgeschichte) Die Kategorien für dieses Modul sollten in der Dokumentation eingetragen werden. Die Interwiki-Links sollten auf Wikidata eingepflegt werden.
- Liste der Unterseiten
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>→[[%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 .. " > "
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