Modul:PageTree: Unterschied zwischen den Versionen
w>PerfektesChaos (fix 2) |
K (27 Versionen von wikivoyage:Modul:PageTree importiert) |
||
| (5 dazwischenliegende Versionen von 3 Benutzern werden nicht angezeigt) | |||
| Zeile 1: | Zeile 1: | ||
--[=[ | local PageTree = { suite = "PageTree", | ||
Module: | 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 | |||
]=] | ]=] | ||
| Zeile 39: | Zeile 47: | ||
local lucky, r | local lucky, r | ||
if s:byte( 1, 1 ) == 47 then -- "/" | if s:byte( 1, 1 ) == 47 then -- "/" | ||
if | if PageTree.suite then | ||
s = | s = PageTree.suite .. s | ||
end | end | ||
end | end | ||
| Zeile 49: | Zeile 57: | ||
return r | return r | ||
end -- facility() | 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() | |||
| Zeile 57: | Zeile 78: | ||
-- Returns true if to be hidden | -- Returns true if to be hidden | ||
local r = false | local r = false | ||
for k, v in pairs( | for k, v in pairs( PageTree.hide ) do | ||
if ask:match( v ) then | if ask:match( v ) then | ||
r = true | r = true | ||
| Zeile 65: | Zeile 86: | ||
return r | return r | ||
end -- fade() | end -- fade() | ||
| Zeile 87: | Zeile 95: | ||
local r, s, title | local r, s, title | ||
local n = 0 | local n = 0 | ||
for k, v in pairs( | for k, v in pairs( PageTree.pages ) do | ||
n = n + 1 | n = n + 1 | ||
s = v.seed | s = v.seed | ||
| Zeile 100: | Zeile 108: | ||
"(-)" .. s ) | "(-)" .. s ) | ||
end | end | ||
elseif | elseif PageTree.linked and | ||
title.isRedirect then | title.isRedirect then | ||
table.insert( redirect, | table.insert( redirect, | ||
| Zeile 139: | Zeile 147: | ||
local r | local r | ||
if adopt:byte( 1, 1 ) == 47 then -- "/" | if adopt:byte( 1, 1 ) == 47 then -- "/" | ||
r = | r = PageTree.start .. adopt:sub( 2 ) | ||
else | else | ||
r = adopt | r = adopt | ||
| Zeile 157: | Zeile 165: | ||
-- Returns absolute page name, or false | -- Returns absolute page name, or false | ||
local designs = { | 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 restrictions = mw.text.split( adopt, ":" ) | ||
local r = "" | local r = "" | ||
| Zeile 216: | Zeile 225: | ||
-- Find parent page | -- Find parent page | ||
-- ancestor -- string, with page name | -- ancestor -- string, with page name | ||
-- Returns page name of parent, or | -- Returns page name of parent, or PageTree.series | ||
local r = ancestor:match( "^(.+)/[^/]+$" ) | local r = ancestor:match( "^(.+)/[^/]+$" ) | ||
if not r then | if not r then | ||
r = ancestor:match( "^([^:]+:).+$" ) | r = ancestor:match( "^([^:]+:).+$" ) | ||
if not r then | if not r then | ||
r = | r = PageTree.series | ||
end | end | ||
end | end | ||
| Zeile 239: | Zeile 248: | ||
local function features( apply, access ) | local function features( apply, access ) | ||
-- Fill | -- Fill PageTree.pages with elements | ||
-- apply -- table, with definitions, read-only | -- apply -- table, with definitions, read-only | ||
-- access -- string, with relative path of module | -- access -- string, with relative path of module | ||
| Zeile 257: | Zeile 266: | ||
if type( v.seed ) == "string" then | if type( v.seed ) == "string" then | ||
s = v.seed | s = v.seed | ||
e = | e = factory( v ) | ||
end | end | ||
end | end | ||
| Zeile 263: | Zeile 272: | ||
if type( v ) == "table" then | if type( v ) == "table" then | ||
s = k | s = k | ||
e = | e = factory( v ) | ||
end | end | ||
elseif k == true then -- root | elseif k == true then -- root | ||
if | if PageTree.pages[ true ] then | ||
bad[ "true" ] = "duplicated" | bad[ "true" ] = "duplicated" | ||
elseif type( v ) == "table" then | elseif type( v ) == "table" then | ||
if type( v.seed ) == "string" then | if type( v.seed ) == "string" then | ||
PageTree.pages[ true ] = factory( v ) | |||
PageTree.pages[ true ].children = { } | |||
else | else | ||
bad[ "true" ] = "seed missing" | bad[ "true" ] = "seed missing" | ||
| Zeile 287: | Zeile 296: | ||
end | end | ||
if s then | if s then | ||
if not | if not PageTree.pages[ s ] then | ||
e.seed = s | e.seed = s | ||
if e.super then | if e.super then | ||
| Zeile 297: | Zeile 306: | ||
end | end | ||
e.children = { } | e.children = { } | ||
PageTree.pages[ s ] = e | |||
end | end | ||
end | end | ||
| Zeile 321: | Zeile 330: | ||
local function feed( access ) | local function feed( access ) | ||
-- Fill | -- Fill PageTree with data, if not yet set | ||
-- access -- string, with relative path of module | -- access -- string, with relative path of module | ||
-- Returns error message, if failed, or false, if fine | -- Returns error message, if failed, or false, if fine | ||
| Zeile 328: | Zeile 337: | ||
local s | local s | ||
if type( r.maxSub ) == "number" then | if type( r.maxSub ) == "number" then | ||
PageTree.maxSub = r.maxSub | |||
end | end | ||
if type( r.stamp ) == "string" then | if type( r.stamp ) == "string" then | ||
if | if PageTree.stamp then | ||
if | if PageTree.stamp < r.stamp then | ||
PageTree.stamp = r.stamp | |||
end | end | ||
else | else | ||
PageTree.stamp = r.stamp | |||
end | end | ||
end | end | ||
| Zeile 342: | Zeile 351: | ||
s = mw.text.trim( r.start ) | s = mw.text.trim( r.start ) | ||
if s ~= "" then | if s ~= "" then | ||
PageTree.start = s | |||
end | end | ||
end | end | ||
if not | if not PageTree.pages then | ||
PageTree.pages = { } | |||
end | end | ||
if type( r.pages ) == "table" then | if type( r.pages ) == "table" then | ||
if not | if not PageTree.pages then | ||
PageTree.pages = { } | |||
end | end | ||
s = features( r.pages, access ) | s = features( r.pages, access ) | ||
| Zeile 393: | Zeile 402: | ||
r = string.format( "%s %s", r, about.suffix ) | r = string.format( "%s %s", r, about.suffix ) | ||
end | end | ||
if | if PageTree.linked and type( about.shift ) == "string" then | ||
r = string.format( "%s <small>→[[%s]]</small>", | r = string.format( "%s <small>→[[%s]]</small>", | ||
r, fair( about.shift ) ) | r, fair( about.shift ) ) | ||
end | end | ||
if | if PageTree.limit and type( about.protection ) == "string" then | ||
r = string.format( "%s %s", | r = string.format( "%s %s", | ||
r, fasten( about.protection ) ) | r, fasten( about.protection ) ) | ||
| Zeile 410: | Zeile 419: | ||
-- adjust -- string, to be standardized | -- adjust -- string, to be standardized | ||
-- Returns string with key | -- Returns string with key | ||
if not Sort then | if not PageTree.Sort then | ||
r, Sort = pcall( require, "Module:Sort" ) | r, PageTree.Sort = pcall( require, "Module:Sort" ) | ||
if type( Sort ) == "table" then | if type( PageTree.Sort ) == "table" then | ||
Sort = Sort.Sort() | PageTree.Sort = PageTree.Sort.Sort() | ||
else | else | ||
error( "Module:Sort not ready" ) | error( "Module:Sort not ready" ) | ||
end | end | ||
end | end | ||
return string.upper( Sort.lex( adjust, "latin", false ) ) | return string.upper( PageTree.Sort.lex( adjust, "latin", false ) ) | ||
end -- filter() | end -- filter() | ||
| Zeile 465: | Zeile 474: | ||
-- a2 -- string, with page name | -- a2 -- string, with page name | ||
-- Returns true if a1 < a2 | -- Returns true if a1 < a2 | ||
local e1 = | local e1 = PageTree.pages[ a1 ] | ||
local e2 = | local e2 = PageTree.pages[ a2 ] | ||
local r | local r | ||
if e1.index then | if e1.index then | ||
| Zeile 488: | Zeile 497: | ||
-- ahead -- string, with syntax in case of .lazy | -- ahead -- string, with syntax in case of .lazy | ||
local r | local r | ||
if | if PageTree.lazy then | ||
r = ":" | r = ":" | ||
else | else | ||
| Zeile 495: | Zeile 504: | ||
return r | return r | ||
end -- flag() | 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() | |||
| Zeile 502: | Zeile 587: | ||
-- acquire -- string, with page name | -- acquire -- string, with page name | ||
if type( acquire ) == "string" then | if type( acquire ) == "string" then | ||
local e = | local e = PageTree.pages[ acquire ] | ||
local s = false | local s = false | ||
if e then | if e then | ||
| Zeile 516: | Zeile 601: | ||
e = { children = { }, | e = { children = { }, | ||
seed = acquire } | seed = acquire } | ||
PageTree.pages[ acquire ] = e | |||
end | end | ||
e.super = s | e.super = s | ||
| Zeile 533: | Zeile 618: | ||
local function fluent() | local function fluent() | ||
-- Collect all .children; add .super where missing | -- Collect all .children; add .super where missing | ||
local let = true | |||
local e | local e | ||
for k, v in pairs( PageTree.pages ) do | |||
for k, v in pairs( | |||
if v.super == nil then | if v.super == nil then | ||
flow( k ) | flow( k ) | ||
elseif not | elseif not PageTree.pages[ v.super ] then | ||
flow( v.super ) | flow( v.super ) | ||
end | end | ||
end -- for k, v | end -- for k, v | ||
for k, v in pairs( | for k, v in pairs( PageTree.pages ) do | ||
if | if PageTree.level then | ||
let = ( not v.seed:find( "/" ) ) | let = ( not v.seed:find( "/" ) ) | ||
end | end | ||
if let and v.super then | if let and v.super then | ||
e = | e = PageTree.pages[ v.super ] | ||
if e then | if e then | ||
table.insert( e.children, k ) | table.insert( e.children, k ) | ||
| Zeile 565: | Zeile 650: | ||
-- all -- true if all grandchildren shall be shown | -- all -- true if all grandchildren shall be shown | ||
-- Returns string with story | -- Returns string with story | ||
local n = table.maxn( above.children ) | local n = table.maxn( above.children ) | ||
local r = "" | local r = "" | ||
if n > 0 then | if n > 0 then | ||
local e | local e, let, lift | ||
local start = "\n" .. string.rep( ahead, amount ) | local start = "\n" .. string.rep( ahead, amount ) | ||
table.sort( above.children, firstly ) | table.sort( above.children, firstly ) | ||
for i = 1, n do | for i = 1, n do | ||
e = | e = PageTree.pages[ above.children[ i ] ] | ||
lift = ( all or above.long ) | lift = ( all or above.long ) | ||
if e.list == false then | if e.list == false then | ||
let = | let = PageTree.list | ||
elseif | elseif PageTree.hide then | ||
let = not fade( e.seed ) | let = not fade( e.seed ) | ||
else | else | ||
| Zeile 585: | Zeile 669: | ||
r = string.format( "%s%s%s", | r = string.format( "%s%s%s", | ||
r, start, field( e, false ) ) | r, start, field( e, false ) ) | ||
if lift and not e.less then | if lift and ( all or not e.less ) then | ||
r = r .. follow( ahead, amount + 1, e, all ) | r = r .. follow( ahead, amount + 1, e, all ) | ||
end | end | ||
| Zeile 598: | Zeile 682: | ||
local function formatAll() | local function formatAll() | ||
-- Render as single list of entries | -- Render as single list of entries | ||
local collect = { } | local collect = { } | ||
local n = 0 | local n = 0 | ||
for k, v in pairs( | local r, let | ||
for k, v in pairs( PageTree.pages ) do | |||
let = true | let = true | ||
if v.list == false and | if v.list == false and | ||
( not | ( not PageTree.list or v.loose or k == true ) then | ||
let = false | let = false | ||
elseif | elseif PageTree.level and v.seed:find( "/" ) then | ||
let = false | let = false | ||
elseif | elseif PageTree.hide then | ||
let = not fade( v.seed ) | let = not fade( v.seed ) | ||
end | end | ||
| Zeile 616: | Zeile 699: | ||
v.show = nil | v.show = nil | ||
end | end | ||
if | if PageTree.light then | ||
local j, k = v.seed:find( | local j, k = v.seed:find( PageTree.start ) | ||
if j == 1 then | if j == 1 then | ||
v.show = v.seed:sub( k + 1 ) | v.show = v.seed:sub( k + 1 ) | ||
| Zeile 628: | Zeile 711: | ||
if n > 0 then | if n > 0 then | ||
local start | local start | ||
local long = ( not | local long = ( not PageTree.light ) | ||
if | if PageTree.lineup then | ||
start = " * " | start = " * " | ||
else | else | ||
| Zeile 647: | Zeile 730: | ||
return r | return r | ||
end -- formatAll() | 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() | |||
| Zeile 654: | Zeile 772: | ||
-- ancestor -- string, with name of root element, or false | -- ancestor -- string, with name of root element, or false | ||
-- Returns string with story | -- Returns string with story | ||
local sup = | local sup = PageTree.self | ||
local higher, i, r | local higher, i, r | ||
if ancestor then | if ancestor then | ||
higher = | higher = PageTree.pages[ ancestor ] | ||
if type( higher ) == "table" then | if type( higher ) == "table" then | ||
higher.super = false | higher.super = false | ||
end | end | ||
else | else | ||
local point = | local point = PageTree.pages[ sup ] | ||
if not point then | if not point then | ||
sup = true | sup = true | ||
elseif point.list == false then | elseif point.list == false then | ||
higher = | higher = PageTree.pages[ sup ] | ||
if type( higher ) == "table" then | if type( higher ) == "table" then | ||
if not higher.loose then | if not higher.loose then | ||
| Zeile 676: | Zeile 794: | ||
end | end | ||
end | end | ||
for i = | for i = PageTree.maxSub, 0, -1 do | ||
higher = | higher = PageTree.pages[ sup ] | ||
if type( higher ) == "table" then | if type( higher ) == "table" then | ||
higher.long = true | higher.long = true | ||
| Zeile 707: | Zeile 825: | ||
local n = 1 | local n = 1 | ||
local reverse = { } | local reverse = { } | ||
local sup = | local sup = PageTree.self | ||
local r | local r | ||
if type( sup ) == "string" and not sup:find( "/", 1, true ) then | if type( sup ) == "string" and not sup:find( "/", 1, true ) then | ||
flow( sup ) | flow( sup ) | ||
repeat | repeat | ||
higher = | higher = PageTree.pages[ sup ] | ||
if type( higher ) == "table" then | if type( higher ) == "table" then | ||
sup = higher.super | sup = higher.super | ||
| Zeile 721: | Zeile 839: | ||
elseif sup then | elseif sup then | ||
n = n + 1 | n = n + 1 | ||
if n > | if n > PageTree.maxSub then | ||
reverse[ n ] = { seed = "???????" } | reverse[ n ] = { seed = "???????" } | ||
break -- repeat | break -- repeat | ||
| Zeile 776: | Zeile 894: | ||
r = true | r = true | ||
end | end | ||
r = | r = PageTree.pages[ r ] | ||
if r then | if r then | ||
r = follow( flag( "#" ), 1, r, true ) | r = follow( flag( "#" ), 1, r, true ) | ||
| Zeile 795: | Zeile 913: | ||
type( args.service ) == "string" and | type( args.service ) == "string" and | ||
type( args.suite ) == "string" then | type( args.suite ) == "string" then | ||
PageTree.series = args.series | |||
PageTree.service = args.service | |||
PageTree.suite = args.suite | |||
if type( args.hide ) == "table" then | if type( args.hide ) == "table" then | ||
PageTree.hide = args.hide | |||
elseif type( args.suppress ) == "string" then | elseif type( args.suppress ) == "string" then | ||
PageTree.hide = { } | |||
table.insert( | table.insert( PageTree.hide, args.suppress ) | ||
end | end | ||
if | if PageTree.series:match( "[:/]$" ) then | ||
PageTree.start = args.series | |||
else | else | ||
PageTree.start = args.series .. "/" | |||
end | end | ||
r = feed( "/" .. | r = feed( "/" .. PageTree.series ) | ||
if r then | if r then | ||
r = fault( r ) | r = fault( r ) | ||
else | else | ||
local life = true | local life = true | ||
if | if PageTree.service == "path" or | ||
PageTree.service == "subpages" then | |||
if args.self then | if args.self then | ||
PageTree.self = args.self | |||
else | else | ||
PageTree.page = mw.title.getCurrentTitle() | |||
PageTree.self = PageTree.page.prefixedText | |||
end | end | ||
if not | if not PageTree.pages[ PageTree.self ] then | ||
if type( | if type( PageTree.pages[ true ] ) == "table" then | ||
PageTree.self = true | |||
else | else | ||
life = false | life = false | ||
| Zeile 831: | Zeile 949: | ||
end | end | ||
if life then | if life then | ||
if | if PageTree.service == "subpages" then | ||
r = formatSub( args.subpager, args.frame ) | r = formatSub( args.subpager, args.frame ) | ||
elseif | elseif PageTree.service == "check" then | ||
PageTree.linked = args.linked | |||
r = failures() | r = failures() | ||
else | else | ||
for k, v in pairs( | for k, v in pairs( PageTree.toggles ) do | ||
PageTree[ v ] = args[ v ] | |||
end -- for k, v | end -- for k, v | ||
if | if PageTree.service == "all" then | ||
r = formatAll() | r = formatAll() | ||
else | else | ||
| Zeile 846: | Zeile 964: | ||
if type( args.segment ) == "string" then | if type( args.segment ) == "string" then | ||
segment = fair( args.segment ) | segment = fair( args.segment ) | ||
if not | if not PageTree.pages[ segment ] then | ||
PageTree.pages[ segment ] = | |||
{ seed = segment, | { seed = segment, | ||
children = { }, | children = { }, | ||
| Zeile 855: | Zeile 973: | ||
end | end | ||
fluent() | fluent() | ||
if | if PageTree.service == "path" then | ||
r = formatPath( segment ) | r = formatPath( segment ) | ||
elseif PageTree.service == "expand" then | |||
r = formatExpand( segment, args ) | |||
else | else | ||
if args.limit == "1" or | if args.limit == "1" or | ||
args.limit == true then | args.limit == true then | ||
PageTree.limit = true | |||
end | end | ||
r = formatTree( segment ) | r = formatTree( segment ) | ||
end | end | ||
end | end | ||
if r and args.stamped and | if r and args.stamped and PageTree.stamp then | ||
local babel = mw.language.getContentLanguage() | local babel = mw.language.getContentLanguage() | ||
local stamp = babel:formatDate( args.stamped, | local stamp = babel:formatDate( args.stamped, | ||
PageTree.stamp ) | |||
r = stamp .. r | r = stamp .. r | ||
end | end | ||
| Zeile 898: | Zeile 1.018: | ||
local lucky | local lucky | ||
params.frame = frame | params.frame = frame | ||
for k, v in pairs( | for k, v in pairs( PageTree.strings ) do | ||
if pars[ v ] and pars[ v ] ~= "" then | if pars[ v ] and pars[ v ] ~= "" then | ||
params[ v ] = pars[ v ] | params[ v ] = pars[ v ] | ||
end | end | ||
end -- for k, v | end -- for k, v | ||
for k, v in pairs( | for k, v in pairs( PageTree.toggles ) do | ||
if pars[ v ] then | if pars[ v ] then | ||
params[ v ] = ( pars[ v ] == "1" ) | params[ v ] = ( pars[ v ] == "1" ) | ||
| Zeile 920: | Zeile 1.040: | ||
return r | return r | ||
end -- framed() | 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() | |||
| Zeile 938: | Zeile 1.095: | ||
function p.check( frame ) | function p.check( frame ) | ||
return framed( frame, "check" ) | return framed( frame, "check" ) | ||
end -- p. | end -- p.check | ||
function p.expand( frame ) | |||
return framed( frame, "expand" ) | |||
end -- p.expand | |||
function p.path( frame ) | function p.path( frame ) | ||
| Zeile 961: | Zeile 1.122: | ||
-- .limit -- show restrictions | -- .limit -- show restrictions | ||
local lucky, r = pcall( forward, args ) | local lucky, r = pcall( forward, args ) | ||
return r or | return r or PageTree | ||
end -- p.test() | 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 | return p | ||
Aktuelle Version vom 9. Februar 2023, 15:00 Uhr
Die Dokumentation für dieses Modul kann unter Modul:PageTree/doc erstellt werden
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