Nicht angemeldeter Benutzer - Bearbeiten von Seiten ist nur als angemeldeter Benutzer möglich.

Modul:Shortcuts

Aus imedwiki
Zur Navigation springen Zur Suche springen

Die Dokumentation für dieses Modul kann unter Modul:Shortcuts/Doku erstellt werden

--[=[ 2014-06-03
Module:Shortcuts
]=]



-- local globals
local Config
local Current = { }
local Errors  = false
local Sort    = false



local function faces( any )
    -- Retrieve single shortcut names
    --     any  -- string, with shortcut name list
    -- Returns table with shortcut page names
    local s = any:gsub( "<[^>]+>", "," )
                 :gsub( "%[%[", "" )
                 :gsub( "%]%]", "" )
    local r = mw.text.split( s, "%s*,%s*" )
    for k, v in pairs( r ) do
        if mw.text.trim( v ) == "" then
            r[ k ] = nil
        end
    end    -- for k, v
    return r
end -- faces()



local function facet( area, assign, assigned )
    -- Create mapping shortcut->target
    --     area      -- string, with target namespace name and colon
    --     assign    -- string, with target page name
    --     assigned  -- string, with shortcut page name
    -- Returns table with entry
    --     .shift     -- string, with target page name
    --     .shortcut  -- string, with shortcut page name
    --     .nsn       -- number, of shortcut namespace
    --     .sort      -- string, with sortable shortcut page title
    local space, subject = assigned:match( "^([^:]+):(.+)$" )
    local r = { }
    r.shift    = area .. assign
    r.shortcut = assigned
    if space then
        local o = mw.site.namespaces[ space ]
        if o then
            r.nsn = o.id
        end
    end
    if not r.nsn then
        r.nsn   = 0
        subject = assigned
    end
    if Sort then
        subject = Sort.lex( subject, "latin", false )
    end
    r.sort = string.upper( subject )
    return r
end -- facet()



local function factory( account, alone, assembly )
    -- Retrieve mappings shortcut->target for entire target namespace
    --     account   -- string, with module name
    --     alone     -- number, of namespace
    --     assembly  -- table, collecting assignments
    -- Extending assembly
    local space = mw.site.namespaces[ alone ]
    if space then
        local got
        local sub = string.format( "%s/%d", account, alone )
        local l, t = pcall( mw.loadData, sub )
        if type( t ) == "table" then
            if space.id == 0 then
                space = ""
            else
                space = space.name .. ":"
            end
            for k, v in pairs( t ) do
                got = faces( v )
                for i, s in pairs( got ) do
                    table.insert( assembly,  facet( space, k, s ) )
                end    -- for i, s
            end    -- for k, v
        end
    end
end -- factory()



local function fair( above )
    -- Convert shortcut list namespaces into talk pages
    --     alert  -- string, with shortcuts
    -- Returns converted shortcuts
    local r = " " .. above
    if type( Config.talks ) == "table" then
        local seek, shift
        for k, v in pairs( Config.talks ) do
            seek  = string.format( "(%%A?)(%s):", k )
            shift = string.format( "%%1%s:", v )
            r = r:gsub( seek, shift )
        end    -- for k, v
    end
    return mw.text.trim( r )
end -- fair()



local function fault( alert, absent )
    -- Format message with class="error"; add Config (category etc.)
    --     alert   -- string, with message
    --     absent  -- boolean, hide message, trigger category
    -- Returns message with markup
    local r = string.format( "<span class=\"error\">%s</span>", alert )
    if absent  and  type( Config ) == "table" then
        local s = Config.suppress
        if type( s ) == "string"  and  s:find( "%s", 1, true ) then
            r = string.format( s, r )
        end
        if type( Config.scream ) == "string" then
            r = string.format( "%s[[Category:%s]]", r, Config.scream )
        end
    end
    return r
end -- fault()



local function failure( assigned, about )
    -- Add one message to Errors
    --     assigned  -- string, with shortcut page name
    --     about     -- string, with error keyword
    if type( Errors ) ~= "table" then
        Errors = { }
    end
    Errors[ assigned ] = about
end -- failure()



local function fiat( ahead, after )
    -- Format table row
    --     ahead  -- string, with first cell
    --     after  -- string or false, with second cell
    -- Returns table row markup
    local r = string.format( "\n|-\n|%s", ahead )
    if after then
        r = string.format( "%s||%s", r, after )
    end
    return r
end -- fiat()



local function first( a1, a2 )
    -- Compare a1 with a2 in reverse title order
    --     a1     -- table, with assignment
    --     a2     -- table, with assignment
    -- Returns true if a1 < a2
    local r
    if  a1.shortcut == a2.shortcut then
        r = ( string.upper( a1.shift )  >  string.upper( a2.shift ) )
    elseif a1.sort == a2.sort then
        r = ( a1.nsn > a2.nsn )
    else
        r = ( a1.sort > a2.sort )
    end
    return r
end -- first()



local function flag( arglist, alone, achieved )
    -- Analyze one shortcut
    --     arglist  -- table, with parameters
    --         .nsn    -- number, of current namespace
    --         .nsns   -- number, of subject namespace
    --     alone     -- string, with shortcut page name
    --     achieved  -- table, with title objects; will be extended
    -- Adds error message to collection
    local page = mw.title.new( alone )
    if page.exists then
        if page.isRedirect then
            local story    = page:getContent()
            local shift    = story:match( "^#[^%[]+%[%[([^%]\n]+)%]%]" )
            local redirect = mw.title.new( shift )
            if mw.title.equals( Current.page, redirect ) then
                for k, v in pairs( achieved ) do
                    if mw.title.equals( page, v ) then
                        failure( alone, "duplicated" )
                        page = false
                        break    -- for k, v
                    end
                end    -- for k, v
                if page then
                    table.insert( achieved, page )
                end
                if arglist.nsn == arglist.nsns
                   and  Config.signature
                   and  not story:find( Config.signature, 15, true ) then
                    local say
                    if type( Config.signal ) == "string" then
                        say = Config.signal
                    else
                        say = ".signature (category) missing"
                    end
                    failure( alone, say )
                end
            else
                failure( alone, "wrong target" )
            end
        else
            failure( alone, "regular page" )
        end
    else
        failure( alone, "missing" )
    end
end -- flag()



local function flash( account, alone )
    -- Create all item table body with two columns; shortcut and target
    --     account   -- string, with module name
    --     alone     -- number or false, with namespace limitation
    -- Returns table rows until end
    local r = "\n|}"
    local collect = { }
    if type( alone ) == "number" then
        factory( account, alone, collect )
    elseif type( Config.rooms ) == "table" then
        for k, v in pairs( Config.rooms ) do
            factory( account, v, collect )
        end    -- for k, v
    else
        r       = r .. "'Config.rooms' not found"
        collect = false
    end
    if collect then
        local second, shortcut
        if not Sort then
            local l, t = pcall( require, "Module:Sort" )
            if type( t ) == "table" then
                Sort = t.Sort()
            end
        end
        table.sort( collect, first )
        for k, v in pairs( collect ) do
            shortcut = string.format( "[[%s]]", v.shortcut )
            second   = string.format( "[[:%s]]", v.shift )
            r        = fiat( shortcut, second ) .. r
        end    -- for k, v
    end
    return r
end -- flash()



local function folder( arglist )
    -- Present table row
    --     arglist  -- table, with parameters
    --         .self       -- string, target page
    --         .story      -- string or nil, append to target page link
    --         .suffix     -- string or nil, append to shortcut list
    -- Returns table row markup
    local l, nsn, shortcuts, t
    local space, subject = arglist.self:match( "^([^:]+):(.+)$" )
    if space then
        local o = mw.site.namespaces[ space ]
        if o then
            nsn = o.id
        else
            nsn = 0
        end
    else
        nsn = 0
    end
    t = string.format( "%s/%d", arglist.suite, nsn )
    l, t = pcall( mw.loadData, t )
    if type( t ) == "table" then
        shortcuts = t[ subject ]
    end
    subject = string.format( "[[:%s]]", arglist.self )
    if type( arglist.story ) == "string" then
        subject = subject .. arglist.story
    end
    if type( shortcuts ) == "string" then
        if shortcuts:sub( 1, 2 ) ~= "[[" then
            local got = faces( shortcuts )
            shortcuts = ""
            for k, v in pairs( got ) do
                shortcuts = string.format( "%s, [[%s]]", shortcuts, v )
            end    -- for k, v
            shortcuts = shortcuts:sub( 3 )
        end
        if type( arglist.suffix ) == "string" then
            shortcuts = shortcuts .. arglist.suffix
        end
    else
        shortcuts = fault( "no shortcuts", false )
    end
    return fiat( subject, shortcuts )
end -- folder()



local function follow( arglist )
    -- Analyze list of shortcuts in single page context
    --     arglist  -- table, with parameters
    --         .shortcuts  -- string, comma separated list of shortcuts
    --         .leave      -- true, if dummy entry
    -- Throws error with message, else returns string with text
    local style = Config.style
    local r
    if type( style ) == "string"  and  style:find( "%s", 1, true ) then
        local pages = { }
        local got   = faces( arglist.shortcuts )
        r = string.format( style, arglist.shortcuts )
        for k, v in pairs( got ) do
            if not arglist.leave then
                flag( arglist, v, pages )
            end
        end    -- for k, v
        if Errors then
            local s = ""
            for k, v in pairs( Errors ) do
                s = string.format( "%s [[%s]]:&#160;%s", s, k, v )
            end    -- for k, v
            r = r .. fault( s, true )
        end
    else
        error( "/config 'style' invalid" )
    end
    return r
end -- follow()



local function forward( args )
    -- Perform task
    --     args  -- table, with parameters
    --         .self       -- string, target page
    --         .shortcuts  -- string, comma separated list of shortcuts
    -- Throws error with message, else returns string with text
    local l, t, sub
    local lucky = false
    local r     = false
    if type( args.suite ) == "string" then
        sub = args.suite .. "/config"
        l, t = pcall( mw.loadData, sub )
        if type( t ) == "table" then
            Config = t
        else
            r = string.format( "'%s' invalid", sub )
        end
    else
        r = "bad .suite"
    end
    if Config then
        if args.self then
            Current.self = args.self
            Current.page = mw.title.new( args.self )
            if not Current.page.exists then
                Current.page = false
                r            = string.format( "'%s' not found",
                                              args.self )
            end
        elseif args.service == "trow" then
            r = "'1=' missing"
        elseif args.service == "total" then
            Current.page = false
            lucky        = true
        else
            Current.page = mw.title.getCurrentTitle()
            Current.self = Current.page.prefixedText
            args.self    = Current.self     -- OBSOLETING
        end
        if Current.page then
            Current.nsn = Current.page.namespace
            args.nsn = Current.nsn     -- OBSOLETING
            if args.nsn % 2 == 0 then
                args.nsns = args.nsn
                args.nsnt = args.nsn + 1
            else
                args.nsnt = args.nsn
                args.nsns = args.nsn - 1
            end
            sub = string.format( "%s/%d", args.suite, args.nsns )
            l, t = pcall( mw.loadData, sub )
            if type( t ) == "table" then
                sub = Current.page.text
                if type( t[ sub ] ) == "string" then
                    if args.shortcuts  and  not Config.locally then
                        args.shortcuts = false
                        r              = "'1=' not permitted"
                    else
                        args.shortcuts = t[ sub ]
                        if args.nsn == args.nsnt then
                            args.shortcuts = fair( args.shortcuts )
                        end
                    end
                end
            end
            if args.service == "template" then
                if type( args.shortcuts ) == "string" then
                    args.shortcuts = mw.text.trim( args.shortcuts )
                    if args.shortcuts == "" then
                        r = "no shortcuts"
                    else
                        lucky = true
                    end
                elseif Current.page.prefixedText == Config.skip then
                    args.shortcuts = "NS:PT"
                    args.leave     = true
                    lucky          = true
                elseif Config.locally then
                    r = "Shortcuts template '1=' missing"
                end
            elseif args.service == "trow"  or
                   args.service == "total" then
                lucky = true
            else
                r = "bad .service"
            end
        end
        if lucky then
            if args.service == "template" then
                r = follow( args )
            elseif args.service == "trow" then
                r = folder( args )
            elseif args.service == "total" then
                r = flash( args.suite, args.nsn )
            end
        end
    end
    if not lucky then
        r = fault( r, true )
        if args.service == "trow" then
            r = fiat( r, false )
        end
    end
    return r
end -- forward()



local function framed( frame, action )
    -- #invoke call in template environment
    --     action  -- string, with keyword
    -- Returns markup
    local lucky, r
    local params = { service = action,
                     suite   = frame:getTitle() }
    local pars   = frame:getParent().args
    if params.service == "template" then
        params.shortcuts = pars[ 1 ]
    elseif params.service == "trow" then
        params.self   = pars[ 1 ]
        params.story  = pars.story
        params.suffix = pars.suffix
    elseif params.service == "total" then
        params.nsn = pars[ 1 ]
        if params.nsn  and
           params.nsn:match( "^(%d+)$" ) then
            params.nsn = tonumber( params.nsn )
        else
            params.nsn = false
        end
    end
    lucky, r = pcall( forward, params )
    return r
end -- framed()



-- Export
local p = { }

function p.template( frame )
    return  framed( frame, "template" )
end -- p.template

function p.total( frame )
    return  framed( frame, "total" )
end -- p.total

function p.trow( frame )
    return  framed( frame, "trow" )
end -- p.trow

function p.twoletters( frame )
    return  framed( frame, "twoletters" )
end -- p.twoletters

function p.test( args )
    -- Debugging
    --     args  -- table, with arguments; mandatory:
    --              .suite      -- Module path
    --              .service    -- action mode, like "template"
    --              .shortcuts  -- list
    --              .self       -- (target) page name
    local lucky, r = pcall( forward, args )
    return r
end -- p.test()

return p