Zum Inhalt springen

Modul:Sort/cellDate

aus Wikipedia, der freien Enzyklopädie
Dies ist eine alte Version dieser Seite, zuletzt bearbeitet am 7. April 2020 um 21:41 Uhr durch PerfektesChaos (Diskussion | Beiträge) (2020-04-04). Sie kann sich erheblich von der aktuellen Version unterscheiden.
Vorlagenprogrammierung Diskussionen Lua Test Unterseiten
Modul Deutsch English

Modul: Dokumentation

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

Dies ist die (produktive) Mutterversion eines global benutzten Lua-Moduls.
Wenn die serial-Information nicht übereinstimmt, müsste eine Kopie hiervon in das lokale Wiki geschrieben werden.
Versionsbezeichnung auf WikiData: 2024-06-01

Updating notwendig

(lokal: 2020-04-04)

local Sort = { suite   = "Sort",
               sub     = "cellDate",
               serial  = "2020-04-04",
               item    = 0,
               globals = { DateTime = 20652535 } }
--[=[
Sort/cellDate -- support table cells with sortable date and time
]=]
local Failsafe  = Sort
local GlobalMod = Sort
local Cell



Sort.mpz        = 0.5
Sort.maxYear    = 2099
Sort.similar    = mw.ustring.char( 8776 )    -- ~~
Sort.supreme    = mw.ustring.char( 8734 )    -- infinit
Sort.types      = { "date",
                    "time",
                    "isoDate",
                    "usLongDate" }
Sort.weights    = { }
Sort.weights.en = {
    [true]           = Sort.similar .. "abeus",
    ["before"]       = 3,
    ["begin"]        = 4,
    ["begin of"]     = 4,
    ["beginning"]    = 4,
    ["beginning of"] = 4,
    ["since"]        = 6,
    ["until"]        = 7,
    ["end of"]       = 8,
    ["after"]        = 9,
    ["about"]        = true,
    [Sort.similar]   = true
}



local foreignModule = function ( access, advanced, append, alt, alert )
    -- Fetch global module
    -- Precondition:
    --     access    -- string, with name of base module
    --     advanced  -- true, for require(); else mw.loadData()
    --     append    -- string, with subpage part, if any; or false
    --     alt       -- number, of wikidata item of root; or false
    --     alert     -- true, for throwing error on data problem
    -- Postcondition:
    --     Returns whatever, probably table
    -- 2019-10-29
    local storage = access
    local finer = function ()
                      if append then
                          storage = string.format( "%s/%s",
                                                   storage,
                                                   append )
                      end
                  end
    local fun, lucky, r, suited
    if advanced then
        fun = require
    else
        fun = mw.loadData
    end
    GlobalMod.globalModules = GlobalMod.globalModules or { }
    suited = GlobalMod.globalModules[ access ]
    if not suited then
        finer()
        lucky, r = pcall( fun,  "Module:" .. storage )
    end
    if not lucky then
        if not suited  and
           type( alt ) == "number"  and
           alt > 0 then
            suited = string.format( "Q%d", alt )
            suited = mw.wikibase.getSitelink( suited )
            GlobalMod.globalModules[ access ] = suited or true
        end
        if type( suited ) == "string" then
            storage = suited
            finer()
            lucky, r = pcall( fun, storage )
        end
        if not lucky and alert then
            error( "Missing or invalid page: " .. storage, 0 )
        end
    end
    return r
end -- foreignModule()



local face = function ( attribute, assign )
    -- Store preceding attribute
    -- Precondition:
    --     attribute    -- string, attribute name
    --     assign       -- string|nil, for value
    -- Postcondition:
    --     attributes extended
    if type( assign ) == "string" then
        local s = mw.text.trim( assign )
        if s ~= "" then
            Cell = Cell  or  { }
            Cell[ attribute ] = s
        end
    end
end -- face()



local facility = function ()
    -- Retrieve page or project language
    -- Postcondition:
    --     Returns string, downcased
    Sort.contLang = Sort.contLang  or
                    mw.language.getContentLanguage()
    return Sort.contLang:getCode():lower()
end -- facility()



local facing = function ( append )
    -- Prepend preceding attributes
    -- Precondition:
    --     append    -- string|html|nil, thing to be extended
    -- Postcondition:
    --     Returns string, if append is a string or empty
    --     otherwise html is extended
    local r
    if Cell then
        if type( append ) == "table" then
            for k, v in pairs( Cell ) do
                if k == "class" then
                    append:addClass( v )
                elseif k == "css" then
                    append:css( v )
                else
                    append:attr( k, v )
                end
            end -- for k, v
        else
            if Cell.css then
                local s = ""
                for k, v in pairs( Cell.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( "style", s:sub( 2 ) )
                end
                Cell.css = nil
            end
            if append then
                r = "| " .. append
            else
                r = "|"
            end
            for k, v in pairs( Cell ) do
                r = string.format( " %s=\"%s\"%s", k, v, r )
            end -- for k, v
            r = r:sub( 2 )
        end
    else
        r = append
    end
    return r
end -- facing()



local features = function ( assign )
    -- Parse CSS string
    -- Precondition:
    --     assign    -- string, CSS to be parsed
    -- Postcondition:
    --     Cell.css
    local css = mw.text.split( assign, ";" )
    --          Problem: URL; not expected
    local pair, s
    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
                Cell     = Cell  or  { }
                Cell.css = Cell.css  or  { }
                Cell.css[ s ] = pair[ 2 ]
            end
        end
    end -- i = 1, #css
end -- features()



local fold = function ( access, alien, assign )
    -- Retrieve config table
    -- Precondition:
    --     access    -- string, external table
    --     alien     -- string, language code
    --     assign    -- string, local table
    -- Postcondition:
    --     Returns table, or not
    local r
    Sort[ assign ] = Sort[ assign ]  or  { }
    if type( Sort[ assign ][ alien ] ) == "nil" then
        local data = foreignModule( "DateTime", false, "local" )
        if data  and
           type( data[ access ] ) == "table" then
            Sort[ assign ][ alien ] = data[ access ][ alien ]
        else
            Sort[ assign ][ alien ] = false
        end
    end
    if type( Sort[ assign ][ alien ] ) == "table" then
        r = Sort[ assign ][ alien ]
    end
    return r
end -- fold()



local 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 -- following()



local fore = function ( args )
    -- Create and merge sort attribute
    -- Precondition:
    --     args    -- table, parameters
    --                .d         -- table, with date
    --                .infinit   -- number|nil, out of ages, +/-1
    --                .pre       -- string|false, for prefix
    --                .type      -- string|false, for sorting
    -- Postcondition:
    --     attributes extended
    local d = { lang = args.d.lang }
    local latest, least, s
    if args.pre then
        local weights = fold( "sortWeights", d.lang, "weights" )
        local i
        if weights then
            i = weights[ args.pre ]
            if type( i ) == "number" then
                if i < 7 then
                   least = true
                else
                   latest = true
                end
            end
        end
    end
    if args.type == "time" then
        if args.d.hour then
            d.hour = args.d.hour
        elseif least then
            d.hour = 0
        elseif latest then
            d.hour = 24
        else
            d.hour = 12
        end
        if args.d.min then
            d.min = args.d.min
        elseif least then
            d.min = 0
        elseif latest then
            d.min = 59
        else
            d.min = 30
        end
        if args.d.sec then
            d.sec = args.d.sec
        elseif least then
            d.sec = 0
        elseif latest then
            d.sec = 59
        else
            d.sec = 30
        end
    else
        d.year = args.d.year or 0
        if args.d.bc  and  args.type == "isoDate" then
            d.year = 0
        else
            d.year = args.d.year or 0
        end
        if args.d.month then
            d.month = args.d.month
        elseif least then
            d.month = 1
        elseif latest then
            d.month = 12
        else
            d.month = 6
        end
        if args.d.dom then
            d.dom = args.d.dom
        elseif least then
            d.dom = 1
        else
            d.dom = tonumber( Sort.DateTime( d ):format( "t" ) )
            if not latest then
                d.dom = math.floor( 0.5 * d.dom )
            end
        end
    end
    d = Sort.DateTime( d )
    if args.type == "isoDate" then
        s = "c"
    elseif args.type == "time" then
        s = "H:i:s"
    elseif args.type == "usLongDate" then
        d.lang = "en"
        s = "F j, Y H:i:s"
    else
        s = "j M Y"
    end
    if args.d.bc then
        s = s:gsub( "Y", "-Y" )
    end
    s = d:format( s )
    if args.infinit then
        s = s:gsub( tostring( Sort.maxYear ),  "9999" )
    end
    face( "data-sort-value", s )
end -- fore()



local format = function ( args )
    -- Format visible date
    -- Precondition:
    --     args    -- table, parameters
    --                .d          -- table, with date
    --                .pattern    -- string, with format
    --                .pad        -- boolean, for padding
    --                .pre        -- string, for prefix
    --                .post       -- string, for postfix
    --                .type       -- string, for sorting
    -- Postcondition:
    --     Returns string
    local r, templates
    if not args.d.lang then
        args.d.lang = facility()
    end
    if args.pad  and  not args.pre then
        local scheme = args.pattern
        if scheme then
            local templates = fold( "templates",
                                    facility(),
                                    "templates" )
            if templates  and
               type( templates[ scheme ] ) == "table"  and
               type( templates[ scheme ].spec ) == "string" then
                scheme = templates[ scheme ].spec
            end
        end
        if scheme then
            local lift
            if args.type == "time" then
                lift = ( scheme:sub( 1, 1 ) == "G"  and
                         args.d.hour  and
                         args.d.hour < 10 )
            else
                lift = ( scheme:sub( 1, 1 ) == "j"  and
                         args.d.dom  and
                         args.d.dom < 10 )
            end
            if lift then
                Cell     = Cell  or  { }
                Cell.css = Cell.css  or  { }
                if not Sort.shift then
                    if following() then
                        Sort.shift = "left"
                    else
                        Sort.shift = "right"
                    end
                    Sort.shift = "padding-" .. Sort.shift
                end
                Cell.css[ Sort.shift ] = string.format( "%.2fem",
                                                        Sort.mpz )
            end
        end
    end
    r = args.d:format( args.pattern or "*" )
    if args.pre or args.post then
        local e
        if args.pre then
            r = string.format( "%s %s", args.pre, r )
        end
        if args.post then
            r = string.format( "%s %s", r, args.post )
        end
        e = mw.html.create( "span" )
                   :css( "white-space", "nowrap" )
                   :wikitext( r )
        r = tostring( e )
    end
    return r
end -- format()



Sort.f = function ( args )
    -- Create table cell start
    -- Parameter:
    --     args    -- table, parameters
    --                .d          -- string|table, with date
    --                .pattern    -- string, with format
    --                .lang       -- string, for formatting
    --                .pad        -- boolean, for padding
    --                .pre        -- string, for prefix
    --                .post       -- string, for postfix
    --                .cell       -- table|nil, sort environment
    --                .type       -- string, for sorting mode
    --                .class      -- string, for cell attribute
    --                .style      -- string|table, for cell attribute
    --                .id         -- string, for cell attribute
    --                .cat        -- string|nil, for error category
    --                .frame      -- object, if present
    -- Postcondition:
    --     Returns string, or expands .cell
    local r
    Cell = false
    if type( Sort.DateTime ) == "nil" then
        local bib = foreignModule( "DateTime",
                                   true,
                                   false,
                                   Sort.globals.DateTime )
        if bib  and  type( bib.DateTime ) == "function" then
            Sort.DateTime = bib.DateTime()
        else
            Sort.DateTime = false
        end
    end
    if type( args ) == "table"  and  Sort.DateTime then
        local present = { }
        local s
        if type( args.lang ) == "string" then
            present.lang = mw.text.trim( args.lang )
        else
            present.lang  = facility()
        end
        if type( args.d ) == "string"  and
           mw.ustring.find( args.d, Sort.supreme, 1, true ) then
            s = mw.text.trim( args.d )
            if s == Sort.supreme then
                present.infinit = 1
            elseif mw.ustring.len( s ) == 2  and
                   mw.ustring.codepoint( s, 2, 2 ) == 8734 then
                local m = mw.ustring.codepoint( s, 1, 1 )
                if m == 45  or  m == 8722 then
                    present.infinit = -1
                elseif m == 43 then
                    present.infinit = 1
                end
            end
            if present.infinit then
                present.d = { year = Sort.maxYear,
                              lang = present.lang }
                if present.infinit > 0 then
                    present.d.month = 12
                    present.d.dom   = 31
                else
                    present.d.bc    = true
                    present.d.month = 1
                    present.d.dom   = 1
                end
                present.d = Sort.DateTime( present.d )
            end
        end
        if not present.infinit then
            local slim
            if type( args.d ) == "string" then
                slim = mw.text.trim( args.d )
                if slim ~= "" then
                    present.d = slim
                end
            end
            if type( args.pre ) == "string" then
                slim = mw.text.trim( args.pre )
                if slim ~= "" then
                    present.pre = slim
                end
            end
            s = type( present.d )
            if s == "string"  and  not present.pre then
                local weights = fold( "sortWeights",
                                      present.lang,
                                      "weights" )
                if weights  and  weights[ true ] then
                    local sw = weights[ true ]
                    if type( sw ) == "string" then
                        slim = mw.ustring.sub( present.d, 1, 1 )
                        slim = mw.ustring.lower( slim )
                        if mw.ustring.find( sw, slim, 1, true ) then
                            local n
                            for k, v in pairs( weights ) do
                                if type( k ) == "string" then
                                    n    = mw.ustring.len( k )
                                    slim = mw.ustring.sub( present.d,
                                                           1,
                                                           n )
                                    if slim == k then
                                        present.pre = k
                                        present.d   = mw.text.trim(
                                               mw.ustring.sub( present.d,
                                                               n + 1 ) )
                                        break -- for k, v
                                    end
                                end
                            end -- for k, v
                        end
                    end
                end
            end
            if s == "string" then
                if present.d == "" then
                    s = "now"
                else
                    s = present.d
                end
                present.d = Sort.DateTime( s, args.lang )
            elseif s ~= "table" then
                present.d = Sort.DateTime( "now", args.lang )
            end
        end
        if type( present.d ) == "table" then
            if type( args.frame ) == "table" then
                Sort.frame = Sort.frame or args.frame
            end
            s = type( args.style )
            if s == "string" then
                features( args.style )
            elseif s == "table" then
                Cell     = { }
                Cell.css = args.style
            end
            face( "class", args.class )
            face( "id", args.id )
            if type( args.type ) == "string" then
                local n
                s = mw.text.trim( args.type )
                n = #s
                if n > 0 then
                    local sort
                    s = s:lower()
                    for i = 1, #Sort.types do
                        sort = Sort.types[ i ]:sub( 1, n ):lower()
                        if s == sort then
                            present.type = Sort.types[ i ]
                            break    -- for i
                        end
                    end -- i = 1, #Sort.types
                end
            end
            if not present.infinit then
                if type( args.pre ) == "string" then
                    s = mw.text.trim( args.pre )
                    if s ~= "" then
                        present.pre = s
                    end
                end
                if type( args.pattern ) == "string" then
                    s = mw.text.trim( args.pattern )
                    if s ~= "" then
                        present.pattern = s:gsub( "\\ ", " " )
                    end
                end
                s = type( args.pad )
                if s == "string" then
                    present.pad = ( args.pad == "1" )
                elseif s == "boolean" then
                    present.pad = args.pad
                end
                if type( args.post ) == "string" then
                    s = mw.text.trim( args.post )
                    if s ~= "" then
                        present.post = s
                    end
                end
                r = format( present )
            end
            fore( present )
            if type( args.cell ) == "table" then
                facing( present.cell )
                if not present.infinit then
                    present.cell:wikitext( r )
                end
            else
                r = facing( r )
            end
        else
            local e = mw.html.create( "span" )
                             :addClass( "error" )
                             :wikitext( "????" )
            if type( args.cat ) == "string" then
                e:wikitext( string.format( "[[Category:%s]]",
                                           args.cat ) )
            end
            if type( args.cell ) == "table" then
                present.cell:node( e )
            else
                r = tostring( e )
            end
        end
    end
    return r
end -- Sort.f()



Sort.furnish = function ()
    -- Retrieve list of project prefixes
    -- Postcondition:
    --     Returns  string  -- with wikitext list
    --              false   -- if none
    local r, weights
    weights = fold( "sortWeights", facility(), "weights" )
    if weights  and  weights[ true ] then
        local order = { }
        for k, v in pairs( weights ) do
            if type( k ) == "string" then
                table.insert( order, k )
            end
        end -- for k, v
        table.sort( order )
        for i = 1, #order do
            if r then
                r = r .. "\n"
            else
                r = ""
            end
            r = string.format( "%s* %s", r, order[ i ] )
        end -- i = 1, #order
    end
    return r
end -- Sort.furnish()



Failsafe.failsafe = function ( atleast )
    -- Retrieve versioning and check for compliance
    -- Precondition:
    --     atleast  -- string, with required version or "wikidata" or "~"
    --                 or false
    -- Postcondition:
    --     Returns  string  -- with queried version, also if problem
    --              false   -- if appropriate
    -- 2019-10-15
    local last  = ( atleast == "~" )
    local since = atleast
    local r
    if last  or  since == "wikidata" then
        local item = Failsafe.item
        since = false
        if type( item ) == "number"  and  item > 0 then
            local entity = mw.wikibase.getEntity( string.format( "Q%d",
                                                                 item ) )
            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
                    else
                        r = vsn.value
                    end
                end
            end
        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.f = function ( frame )
    -- Template call
    Sort.frame = frame
    return Sort.f( frame.args )  or  ""
end -- p.f

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.furnish = function ()
    return Sort.furnish()  or  ""
end -- p.f

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

return p