Vorlagenprogrammierung Diskussionen Lua Test Unterseiten
Modul Deutsch English

Modul: Dokumentation

Diese Seite enthält Code in der Programmiersprache Lua. Einbindungszahl Cirrus

Weiterleitung der Diskussionsseite fehlt
Diese Seite ist eine lokale Kopie aus de.wikipedia.org und von dort zu aktualisieren.

Versionsbezeichnung auf WikiData: 2024-07-03


local Sort = { suite   = "Sort",
               sub     = "cell",
               serial  = "2024-07-03",
               item    = 90144855 }
--[=[
Sort/cell    --  support table cells sorting modules
]=]
local Failsafe = Sort



local face = function ( assign, attribute, args, apply, allow )
    -- Store preceding attribute
    -- Precondition:
    --     assign       -- table, to extend
    --     attribute    -- string, attribute name
    --     args         -- table|nil, parameters to retrieve
    --     apply        -- string|number|nil, for value
    --     allow        -- boolean, permit and request numbers >(=)0
    -- Postcondition:
    --     attributes extended
    local d, s, v
    if apply then
        d = apply
    elseif args then
        d = args[ attribute ]
    end
    s = type( d )
    if s == "string" then
        d = mw.text.trim( d )
        if d == "" then
            d = false
        end
    end
    if d then
        if allow then
            local min = 1
            if attribute == "rowspan" then
                min = 0
            end
            if s == "string" then
                s = string.format( "[%d-9]%%d*", min )
                if d:match( string.format( "^%s$", s ) ) then
                    v = d
                else
                    local p
                    s = string.format( "^(['\"]) *(%s) *%%1$", s )
                    p, s = d:match( s )
                    if s then
                        v = s
                    end
                end
            elseif s == "number"  and  d >= min then
                local k = math.floor( d )
                if d == k then
                    v = tostring( k )
                end
            end
            if not v then
                assign.scream = attribute .. "=????"
            end
        elseif s == "string" then
            local p
            p, s = d:match( "^(['\"])([^\"]*)%1$" )
            if s then
                d = mw.text.trim( s )
            end
            if d ~= "" then
                if attribute == "dir" then
                    d = d:lower()
                    if d == "ltr"  or  d == "rtl" then
                        v = d
                    end
                else
                    v = d
                end
            end
        end
    end
    if v then
        assign.props = assign.props  or  { }
        assign.props[ attribute ] = v
    end
end -- face()



local facing = function ( args, append )
    -- Prepend preceding attributes
    -- Precondition:
    --     args      -- table, parameters
    --                  .pre        -- string|nil, for starting text
    --                  .preSep     -- string|nil, for prefix separator
    --                  .css     -- string|table|nil, for cell attribute
    --                  .class   -- string|nil, for cell attribute
    --     append    -- string|html|nil, thing to be extended
    -- Postcondition:
    --     Returns string, if append is a string or nil
    --     otherwise html is extended
    local p = args.props
    local r = append
    if p then
        if type( r ) == "table" then
            for k, v in pairs( p ) do
                if k == "class" then
                    r:addClass( v )
                elseif k == "css" then
                    r:css( v )
                else
                    r:attr( k, v )
                end
            end -- for k, v
        else
            local s
            if p.css then
                s = ""
                for k, v in pairs( p.css ) do
                    if type( k ) == "string"  and
                       type( v ) == "string" then
                        v = mw.text.trim( v )
                        k = mw.text.trim( k )
                        if v ~= ""  and
                           k ~= "" then
                            s = string.format( "%s;%s:%s", s, k, v )
                        end
                    end
                end -- for k, v
                if s ~= "" then
                    face( args, "style", false, s:sub( 2 ) )
                end
                p.css = nil
            end
            if args.preSep then
                r = "| " .. Sort.fore( p.pre, p.preSep, r )
            else
                r = "| "  ..  ( r or "" )
            end
            for k, v in pairs( p ) do
                if k ~= "lang"  or  v ~= Sort.facility() then
                    r = string.format( " %s=\"%s\"%s", k, v, r )
                end
            end -- for k, v
            r = r:sub( 2 )
        end
    end
    return r
end -- facing()



local features = function ( args, assign )
    -- Parse CSS string
    -- Precondition:
    --     args      -- table, parameters
    --                  .style    -- string|nil, CSS to be parsed
    --     assign    -- table, to be extended
    -- Postcondition:
    --     args.props.css added
    if args.style then
        local s         = mw.text.trim( args.style )
        local pair, css = s:match( "^(['\"])([^\"]*)%1$" )
        if css then
            s = mw.text.trim( css )
        end
        css = mw.text.split( s, ";" )
        --    Problem: URL; not expected
        for i = 1, #css do
            pair = mw.text.split( css[ i ], ":" )
            --     Problem: URL; not expected
            if #pair == 2 then
                s = mw.text.trim( pair[ 1 ] )
                if s ~= "" then
                    assign.props          = assign.props  or  { }
                    assign.props.css      = assign.props.css  or  { }
                    assign.props.css[ s ] = mw.text.trim( pair[ 2 ] )
                end
            end
        end -- i = 1, #css
    end
end -- features()



Sort.faced = function ( args, assign )
    -- Assign a sortable value
    -- Precondition:
    --     args      -- table, to be extended
    --     assign    -- string, to be memorized
    -- Postcondition:
    --     args is extended, if meaningful
    if type( assign ) == "string" then
        local s = mw.text.trim( assign )
        if s ~= "" then
            s = mw.text.decode( s )
            s = mw.ustring.gsub( s, mw.ustring.char( 160 ), " " )
            s = mw.ustring.gsub( s, "%s+", " " )
            s = s:gsub( "\"", "" )
                 :gsub( "<", "" )
                 :gsub( ">", "" )
            s = mw.ustring.sub( s, 1, 99 )
            face( args, "data-sort-value", false, s )
        end
    end
end -- Sort.faced()



Sort.facility = function ()
    -- Retrieve page or project language
    -- Postcondition:
    --     Returns string, downcased
    if type( Sort.slang ) ~= "string" then
        Sort.contLang = Sort.contLang  or
                        mw.language.getContentLanguage()
        Sort.slang    = Sort.contLang:getCode():lower()
    end
    return Sort.slang
end -- Sort.facility()



Sort.fair = function ( args, access, assign )
    -- Assign a non-empty string
    -- Precondition:
    --     args      -- table, to be queried
    --     access    -- string, to identify a component
    --     assign    -- table, to be extended
    -- Postcondition:
    --     assign is extended, if meaningful
    if type( args[ access ] ) == "string" then
        local s = mw.text.trim( args[ access ] )
        if s ~= "" then
            assign[ access ] = s
        end
    end
end -- Sort.fair()



Sort.fault = function ( alert, args )
    -- Error occurred
    -- Parameter:
    --     alert    -- string, with message
    --     args     -- table, parameters
    --                 .elem    -- table, if mw.html
    --                 .cat     -- string|table|nil, for error category
    --                             may contain one or more mw.title
    -- Postcondition:
    --     Returns string, or expands .elem
    local r, suffix
    local e = mw.html.create( "span" )
                     :addClass( "error" )
                     :wikitext( alert )
    if args.cat then
        local s = type( args.cat )
        local c
        if s == "string" then
            s = mw.text.trim( args.cat )
            if s ~= "" then
                c = { }
                table.insert( c, s )
            end
        elseif s == "table" then
            if type( args.cat.baseText ) == "string" then
                c = { }
                table.insert( c, args.cat.text )
            else
                local v
                for i = 1, #args.cat do
                    v = args.cat[ i ]
                    s = type( v )
                    if s == "string" then
                        s = mw.text.trim( v )
                        if s ~= "" then
                            c = c or { }
                            table.insert( c, s )
                        end
                    elseif s == "table"  and
                           type( v.baseText ) == "string" then
                        c = c or { }
                        table.insert( c, v.text )
                    end
                end -- i = 1, #args.cat
            end
        end
        if c then
            suffix = ""
            for i = 1, #c do
                suffix = string.format( "%s[[Category:%s]]",
                                        suffix, c[ i ] )
            end -- i = 1, #c
        end
    end
    if args.elem then
        if suffix then
            e:wikitext( suffix )
        end
        args.elem:node( e )
    else
        r = tostring( e )
        if suffix then
            r = r .. suffix
        end
    end
    return r
end -- Sort.fault()



Sort.feature = function ( args, access, assign )
    -- Retrieve or set CSS property
    -- Precondition:
    --     args      -- table, parameters
    --     access    -- string, property name
    --     assign    -- string|nil, value to be set
    -- Postcondition:
    --     Returns string, or not, if not assigned
    local r
    if assign then
        args.props               = args.props  or  { }
        args.props.css           = args.props.css  or  { }
        args.props.css[ access ] = assign
    elseif args.props  and
       args.props.css  and
       args.props.css[ access ] then
        r = args.props.css[ access ]
    end
    return r
end -- Sort.feature()



Sort.finalize = function ( args, append )
    -- Complete table cell
    -- Parameter:
    --     args      -- table, parameters
    --                  .props     -- table, if present
    --                  .pre       -- string|nil, with prefix
    --                  .cell      -- true, if mandatory table syntax
    --                  .elem      -- table, if mw.html
    --                  .scream    -- string|nil, with error message
    --     append    -- string|nil, with content
    -- Postcondition:
    --     Returns string, or expands .elem, or nil
    local r
    if args.props then
        if args.elem then
            facing( args, args.elem )
            if append then
                args.elem:wikitext( faced( args, append ) )
            end
        elseif append  and  not args.cell then
            local e = mw.html.create( "span" )
                             :wikitext( append )
            facing( args, e )
            r = tostring( e )
        else
            r = facing( args, append )
        end
    elseif args.elem and append then
        args.elem:wikitext( append )
    else
        r = append
    end
    if args.scream then
        if args.elem then
            Sort.fault( args.scream, args )
        else
            r = r or ""
            r = r .. Sort.fault( args.scream, args )
        end
    end
    return r
end -- Sort.finalize()



Sort.first = function ( args, always )
    -- Initialize table cell start
    -- Parameter:
    --     args    -- table, parameters
    --                .cell       -- string|boolean|table, enforce sort
    --                .rowspan    -- number|string, for cell attribute
    --                .colspan    -- number|string, for cell attribute
    --                .class      -- string, for cell attribute
    --                .style      -- string|table, for cell attribute
    --                .id         -- string, for cell attribute
    --                .lang       -- string, for cell attribute
    --                .dir        -- string, for cell attribute
    --                .frame      -- object, if present
    --     always    -- true, if mandatory table syntax
    -- Postcondition:
    --     Returns table with consolidated parameters
    local r = { }
    local s = type( args.style )
    if s == "string" then
        features( args, r )
    elseif s == "table" then
        r.props     = { }
        r.props.css = args.style
    end
    s = type( args.cell )
    if s == "string" then
        r.cell = ( args.cell == "1" )
    elseif s == "table"  and
           type( args.cell.node ) == "function" then
        r.elem = args.cell
        r.cell = true
    elseif s == "boolean" then
        r.cell = args.cell
    else
        r.cell = always
    end
    if type( args.pre ) == "string" then
        s = mw.text.trim( args.pre )
        if s ~= "" then
            r.pre = s
        end
    end
    if r.cell then
        face( r, "rowspan", args, false, true )
        face( r, "colspan", args, false, true )
    end
    face( r, "class", args )
    face( r, "id", args )
    face( r, "lang", args )
    face( r, "dir", args )
    if type( args.frame ) == "table" then
        r.frame = args.frame
    end
    return r
end -- Sort.first()



Sort.following = function ()
    -- Retrieve text order
    -- Postcondition:
    --     Returns true, if left-to-right
    if type( Sort.ltr ) ~= "boolean" then
        Sort.contLang = Sort.contLang  or
                        mw.language.getContentLanguage()
        Sort.ltr = not Sort.contLang:isRTL()
    end
    return Sort.ltr
end -- Sort.following()



Sort.fore = function ( ahead, amid, after )
    -- Combine prefix and main text
    --     ahead   -- string|nil, with prefix
    --     amid    -- string|nil, with absolute or relative separator
    --                            "-" no space if ahead terminated
    --                                with any hyphen
    --                            " " always space
    --                            else always space
    --     after   -- string|nil, with main text
    -- Postcondition:
    --     Returns string
    local r, s
    if type( after ) == "string" then
        r = mw.text.trim( after )
        if r == "" then
            r = false
        end
    end
    if type( ahead ) == "string" then
        s = mw.text.trim( ahead )
        if s == "" then
            s = false
        end
    end
    if r then
        if s then
            local sep = " "
            if amid == "-" then
                if mw.ustring.len( s ) > 1 then
                    if not Sort.swing then
                        Sort.swing = mw.ustring.char( 91,   39,
                                                          0x2019,
                                                            37, 45,
                                                      93 )
                    end
                    if mw.ustring.find( mw.ustring.sub( s, -1 ),
                                        Sort.swing ) then
                        sep = ""
                    end
                end
            end
            r = string.format( "%s%s%s", s, sep, r )
        end
    elseif s then
        r = s
    end
    return r or ""
end -- Sort.fore()



Sort.formatDate = function ( align, at )
    -- Format local date and time
    --     align    -- string, with format
    --     at       -- string|nil, with timestamp
    -- Postcondition:
    --     Returns string
    Sort.contLang = Sort.contLang  or
                    mw.language.getContentLanguage()
    return Sort.contLang:formatDate( align, at, true )
end -- Sort.following()



Failsafe.failsafe = function ( atleast )
    -- Retrieve versioning and check for compliance
    -- Precondition:
    --     atleast  -- string, with required version
    --                         or wikidata|item|~|@ or false
    -- Postcondition:
    --     Returns  string  -- with queried version/item, also if problem
    --              false   -- if appropriate
    -- 2024-03-01
    local since  = atleast
    local last   = ( since == "~" )
    local linked = ( since == "@" )
    local link   = ( since == "item" )
    local r
    if last  or  link  or  linked  or  since == "wikidata" then
        local item = Failsafe.item
        since = false
        if type( item ) == "number"  and  item > 0 then
            local suited = string.format( "Q%d", item )
            if link then
                r = suited
            else
                local entity = mw.wikibase.getEntity( suited )
                if type( entity ) == "table" then
                    local seek = Failsafe.serialProperty or "P348"
                    local vsn  = entity:formatPropertyValues( seek )
                    if type( vsn ) == "table"  and
                       type( vsn.value ) == "string"  and
                       vsn.value ~= "" then
                        if last  and  vsn.value == Failsafe.serial then
                            r = false
                        elseif linked then
                            if mw.title.getCurrentTitle().prefixedText
                               ==  mw.wikibase.getSitelink( suited ) then
                                r = false
                            else
                                r = suited
                            end
                        else
                            r = vsn.value
                        end
                    end
                end
            end
        elseif link then
            r = false
        end
    end
    if type( r ) == "nil" then
        if not since  or  since <= Failsafe.serial then
            r = Failsafe.serial
        else
            r = false
        end
    end
    return r
end -- Failsafe.failsafe()



-- Export
local p = { }

p.failsafe = function ( frame )
    -- Versioning interface
    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 Failsafe.failsafe( since )  or  ""
end -- p.failsafe

p.Sort = function ()
    -- Module interface
    return Sort
end

setmetatable( p,  { __call = function ( func, ... )
                                 setmetatable( p, nil )
                                 return Failsafe
                             end } )

return p