Modul:PageTree: Unterschied zwischen den Versionen
w>PerfektesChaos (+ .loose) |
w>PerfektesChaos (updates) |
||
Zeile 1: | Zeile 1: | ||
--[=[ 2014- | --[=[ 2014-09-14 | ||
Module:pageTree | Module:pageTree | ||
Zeile 620: | Zeile 620: | ||
local sup = Current.self | local sup = Current.self | ||
local r | local r | ||
flow( | if not sup:find( "/", 1, true ) then | ||
flow( sup ) | |||
repeat | |||
higher = Current.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 > Current.maxSub then | |||
reverse[ n ] = { seed = "???????" } | |||
break -- repeat | |||
end | |||
else | |||
break -- repeat | break -- repeat | ||
end | end | ||
Zeile 638: | Zeile 642: | ||
break -- repeat | break -- repeat | ||
end | end | ||
until not higher | |||
end | |||
if n > 1 then | if n > 1 then | ||
for i = n, 2, -1 do | for i = n, 2, -1 do |
Version vom 14. September 2014, 13:11 Uhr
Die Dokumentation für dieses Modul kann unter Modul:PageTree/doc erstellt werden
--[=[ 2014-09-14 Module:pageTree ]=] -- local globals local Current = { maxSub = 10 } local Sort = false local Strings = { "segment", "self", "stamped", "subpager", "suppress" } local Toggles = { "lazy", "level", "lineup", "light", "linked", "list" } 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 Current.suite then s = Current.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 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( Current.hide ) do if ask:match( v ) then r = true break -- for k, v end end -- for k, v return r end -- fade() local function failsafe( 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 -- failsafe() local function failures() -- Check all pages local redirect = {} local unknown = {} local r, s, title local n = 0 for k, v in pairs( Current.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 Current.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 = Current.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 father( ancestor ) -- Find parent page -- ancestor -- string, with page name -- Returns page name of parent, or Current.series local r = ancestor:match( "^(.+)/[^/]+$" ) if not r then r = ancestor:match( "^([^:]+:).+$" ) if not r then r = Current.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 Current.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 = failsafe( v ) end end elseif s == "string" then if type( v ) == "table" then s = k e = failsafe( v ) end elseif k == true then -- root if Current.pages[ true ] then bad[ "true" ] = "duplicated" elseif type( v ) == "table" then if type( v.seed ) == "string" then Current.pages[ true ] = failsafe( v ) Current.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 Current.pages[ s ] then e.seed = s if e.super then if type( e.super ) == "string" then e.super = fair( e.super ) end else e.super = father( s ) end e.children = { } Current.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 Current 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 Current.maxSub = r.maxSub end if type( r.stamp ) == "string" then if Current.stamp then if Current.stamp < r.stamp then Current.stamp = r.stamp end else Current.stamp = r.stamp end end if type( r.start ) == "string" then s = mw.text.trim( r.start ) if s ~= "" then Current.start = s end end if not Current.pages then Current.pages = { } end if type( r.pages ) == "table" then if not Current.pages then Current.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 -- 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 Current.linked and type( about.shift ) == "string" then r = string.format( "%s <small>→[[%s]]</small>", r, fair( about.shift ) ) 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 Sort then r, Sort = pcall( require, "Module:Sort" ) if type( Sort ) == "table" then Sort = Sort.Sort() else error( "Module:Sort not ready" ) end end return string.upper( 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 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 = Current.pages[ a1 ] local e2 = Current.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 Current.lazy then r = ":" else r = ahead end return r end -- flag() local function flow( acquire ) -- Collect the .super in path -- acquire -- string, with page name if type( acquire ) == "string" then local e = Current.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 } Current.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 e local let = true for k, v in pairs( Current.pages ) do if not v.super then flow( k ) elseif not Current.pages[ v.super ] then flow( v.super ) end end -- for k, v for k, v in pairs( Current.pages ) do if Current.level then let = ( not v.seed:find( "/" ) ) end if let and v.super then e = Current.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 let, lift local n = table.maxn( above.children ) local r = "" if n > 0 then local e local start = "\n" .. string.rep( ahead, amount ) table.sort( above.children, firstly ) for i = 1, n do e = Current.pages[ above.children[ i ] ] lift = ( all or above.long ) if e.list == false then let = Current.list elseif Current.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 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 r local collect = { } local let local n = 0 for k, v in pairs( Current.pages ) do let = true if v.list == false and ( not Current.list or v.loose or k == true ) then let = false elseif Current.level and v.seed:find( "/" ) then let = false elseif Current.hide then let = not fade( v.seed ) end if let then if v.show then v.show = nil end if Current.light then local j, k = v.seed:find( Current.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 Current.light ) if Current.lineup then start = " * " else start = "\n" .. flag( "#" ) end table.sort( collect, first ) 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 formatPath( ancestor ) -- Render tree as partially opened list -- ancestor -- string, with name of root element, or false -- Returns string with story local higher local sup = Current.self local r if ancestor then higher = Current.pages[ ancestor ] if type( higher ) == "table" then higher.super = false end end while true do higher = Current.pages[ sup ] if type( higher ) == "table" then higher.long = true sup = higher.super if not sup then break -- while end else higher = false break -- while end end -- while true 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 = Current.self local r if not sup:find( "/", 1, true ) then flow( sup ) repeat higher = Current.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 > Current.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 = Current.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 Current.series = args.series Current.service = args.service Current.suite = args.suite if type( args.hide ) == "table" then Current.hide = args.hide elseif type( args.suppress ) == "string" then Current.hide = { } table.insert( Current.hide, args.suppress ) end if Current.series:match( "[:/]$" ) then Current.start = args.series else Current.start = args.series .. "/" end r = feed( "/" .. Current.series ) if r then r = fault( r ) else local life = true if Current.service == "path" or Current.service == "subpages" then if args.self then Current.self = args.self else Current.page = mw.title.getCurrentTitle() Current.self = Current.page.prefixedText end if not Current.pages[ Current.self ] then if type( Current.pages[ true ] ) == "table" then Current.self = true else life = false end end end if life then if Current.service == "subpages" then r = formatSub( args.subpager, args.frame ) elseif Current.service == "check" then Current.linked = args.linked r = failures() else for k, v in pairs( Toggles ) do Current[ v ] = args[ v ] end -- for k, v if Current.service == "all" then r = formatAll() else local segment if type( args.segment ) == "string" then segment = fair( args.segment ) if not Current.pages[ segment ] then Current.pages[ segment ] = { seed = segment, children = { }, super = true, list = false } end end fluent() if Current.service == "path" then r = formatPath( segment ) else r = formatTree( segment ) end end if r and args.stamped and Current.stamp then local babel = mw.language.getContentLanguage() local stamp = babel:formatDate( args.stamped, Current.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 lucky = false local r = pars[ 1 ] if r then params.series = mw.text.trim( r ) if params.series == "" then r = false end end if r then params.frame = frame for k, v in pairs( Strings ) do if pars[ v ] and pars[ v ] ~= "" then params[ v ] = pars[ v ] end end -- for k, v for k, v in pairs( Toggles ) do if pars[ v ] then params[ v ] = ( pars[ v ] == "1" ) end end -- for k, v lucky, r = pcall( forward, params ) else r = "'1=' missing" end if not lucky then r = fault( r ) elseif not r then r = "" end return r end -- framed() -- 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.path 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" local lucky, r = pcall( forward, args ) return r or Current end -- p.test() return p