Modul:DateTime/sort

aus Wikipedia, der freien Enzyklopädie
Zur Navigation springen Zur Suche springen

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

--[=[ 2015-08-05
Date and time utilities: Sort
]=]



-- local globals
local DateTime  = false
local Invalid   = "-199999999999999"
local Templates = { }
local World     = { slang       = "en",
                    sortPrefix  = { },
                    sortSep     = 46 }
World.sortPrefix.en  = {
    { "before",       3 },
    { "begin",        4 },
    { "begin of",     4 },
    { "beginning",    4 },
    { "beginning of", 4 },
    { "since",        6 },
    { "until",        7 },
    { "end of",       8 },
    { "after",        9 },
    { "about",        false }
}
local Span0 = "<span style='visibility:hidden;'>0</span>"



local function fault( alert )
    -- Format error message by class=error
    -- Parameter:
    --     alert  -- string, with error message
    -- Returns:
    --     string, with decorated error message
    return string.format( "<span class=\"error\">%s</span>", alert )
end -- fault()



local function favorite( arg )
    -- Test boolean template argument
    -- Parameter:
    --     arg  -- string, trimmed, with switch, or nil
    -- Returns:
    --     boolean, true iff requested
    local r = false
    if arg then
        if #arg > 0 then
            r = ( arg ~= "0" )
        end
    end
    return r
end -- favorite()



local function feed( alien )
    -- Localize, if present
    -- Parameter:
    --     alien  -- string, with language code, or nil
    -- Returns:
    --     table with sortPrefix, iff inital call
    local r = false
    if World.localized then
        r = World.sortPrefix.en
    else
        local lucky, data = pcall( mw.loadData, "Module:DateTime/local" )
        if lucky then
            if alien then
                World.slang = alien
            elseif data.slang then
                World.slang = data.slang
            end
            if data.sortPrefix and data.sortPrefix[ World.slang ] then
                r = data.sortPrefix[ World.slang ]
            end
            if type(data.sortSep) == "number" then
                World.sortSep = data.sortSep
            end
        end
        if not r then
            r = World.sortPrefix.en
        end
        World.localized = true
    end
    return r
end -- feed()



local function fetch( assign )
    -- Retrieve instance
    -- Returns:
    --     DateTime prototype, or false
    -- Throws:
    --     error, if main module unavailable
    local l, r = pcall( require, "Module:DateTime" )
    if l then
        r = r.DateTime()( assign )
    else
        error( fault( "Module:DateTime" ) )
    end
    return r
end -- fetch()



local function fiat()
    -- Create sortkey from DateTime object
end -- fiat()



local function field( assign, amenable, args )
    -- Combine data-sort-value, cell attributes and displayed data
    -- Parameter:
    --     assign    -- string, sortkey
    --     amenable  -- string, displayed content
    --     args      -- table, possible attributes
    --                         -- align
    --                         -- class
    --                         -- id
    --                         -- style
    -- Returns:
    --     string with cell attributes and content
    local start = string.format( "data-sort-value='%s'", assign )
    local style
    local stick
    if args.align then
        local expand = { c = "center",
                         l = "left",
                         r = "right",
                         center = "center",
                         left   = "left",
                         right  = "right" }
        stick = expand[ args.align ]
        if stick then
            string.format( "text-align:%s", stick )
        end
    end
    if args.style then
        style = args.style
        if stick then
            style = string.format( "%s;%s", style, stick )
        end
    elseif stick then
        style = stick
    end
    if style then
        start = string.format( "%s style='%s'", start, style )
    end
    if args.class then
        start = string.format( "%s class='%s'", start, args.class )
    end
    if args.id then
        start = string.format( "%s id='%s'", start, args.id )
    end
    return string.format( "%s|%s", start, amenable )
end -- field()



local function figure( appoint, assign, align )
    -- Create sortkey number from DateTime object
    -- Parameter:
    --     appoint  -- table; valid DateTime object
    --     assign   -- digit, if adapting, or false
    --     align    -- true: leading zeros until 8 digits if positive
    -- Returns:
    --     string with number, or false
    local dom   = appoint.dom
    local month = appoint.month
    local minor = 0
    local start = false
    local r
    if month then
        if assign == 3 and not dom then
            month = month - 1
        end
    else
        if (assign == 8 or assign == 9) and not dom then
            month = 99
        else
            month = 0
            minor = 5
        end
    end
    if dom then
        if assign == 3 or assign == 4 then
            dom = dom - 1
        end
    else
        if assign == 8 or assign == 9 then
            dom = 99
        else
            dom   = 0
            minor = 5
        end
    end
    r = month * 100  +  dom
    if appoint.year then
        r = appoint.year * 10000  +  r
        if align  and  appoint.year < 1000 and not appoint.bc then
            if appoint.year < 100 then
                if appoint.year < 10 then
                    start = "000"
                else
                    start = "00"
                end
            else
                start = "0"
            end
        end
    elseif align then
        if appoint.month  and  month < 10  and  month > 0 then
            start = "00000"
        else
            start = "0000"
        end
    end
    if appoint.bc then
        start = "-"
    end
    r = tostring( r )
    if start then
        r = start .. r
    end
    if assign then
        minor = assign
    end
    if appoint.hour then
        feed()
        minor = minor  +  100000 * appoint.hour
        if appoint.min then
            minor = minor  +  1000 * appoint.min
            if appoint.sec then
                minor = minor  +  10 * appoint.sec
            end
        end
        r = string.format( "%s%c%07d",
                           r, World.sortSep, minor )
    elseif minor > 0 then
        feed()
        r = string.format( "%s%c%d",
                           r, World.sortSep, minor )
    end
    return r
end -- figure()



local function flag( ahead, append )
    -- Combine invisible sortkey with displayed data
    -- Parameter:
    --     ahead   -- string, sortkey
    --     append  -- string, displayed content
    -- Returns:
    --     string with cell content
    local s = "<span style='display:none' class='sortkey'>%s</span>%s"
    return string.format( s, ahead, append )
end -- flag()



local function focus( analyse, assign )
    -- Retrieve numeric sort value from date string
    -- Parameter:
    --     analyse  -- string, with date; time may be appended
    --     assign   -- digit, if adapting, or false
    -- Returns:
    --     string with number, or false
    local r = fetch( analyse )
    if r then
        r = figure( r, assign, true )
    end
    return r
end -- focus()



local function fore( analyse, alien )
    -- Retrieve numeric sort value from probably prefixed string
    -- Parameter:
    --     analyse  -- string, trimmed, for dating, letter at beginning
    --     alien    -- string, with language code, or nil
    -- Returns:
    --     string with number, or false
    local prefix = feed( alien )
    local sort   = analyse:gsub( "&nbsp;", " " )
                          :gsub( "&#160;", " " )
                          :gsub( "&#32;", " " )
    local move   = false
    if prefix then
        local lift   = mw.ustring.match( analyse, "^%u" )
        local n, scan
        for k, v in pairs( prefix ) do
            scan = v[ 1 ]
            if lift then
                scan = mw.ustring.upper( mw.ustring.sub( scan, 1, 1 ) )
                       .. mw.ustring.sub( scan, 2 )
            end
            n = mw.ustring.len( scan )
            if mw.ustring.sub( sort, 1, n ) == scan then
                sort = mw.text.trim( mw.ustring.sub( sort,  n + 1 ) )
                move = v[ 2 ]
                break -- for k, v
            end
        end -- for k, v
    end
    return focus( sort, move )
end -- fore()



local function fruit( analyse, alien )
    -- Retrieve numeric sort value
    -- Parameter:
    --     analyse  -- string, trimmed, for dating
    --     alien    -- string, with language code, or nil
    -- Returns:
    --     string with number
    local stamp = analyse:gsub( "{{0}}", "" )
    local k     = mw.ustring.codepoint( stamp, 1, 1 )
    local r     = false
    if not k then    -- analyse is empty
        k     = 48
        stamp = "now"
    end
    if k == 45 then    -- ASCII hyphen minus
        if stamp:match( "^%-%d+$" ) then
            r = stamp .. "0000"
        end
    elseif k >= 48  and  k <= 57 then    -- ASCII digit
        r = focus( stamp, false )
    elseif mw.ustring.match( stamp, "^%a" ) then    -- any letter
        r = fore( stamp, alien )
    end
    if type( r ) == "number" then
        r = tostring( r )
    elseif not r then
        r = Invalid
    end
    return r
end -- fruit()



Templates.SD = function ( args, advanced )
    -- Support Template:SD
    -- Parameter:
    --    args     -- table, template parameters; each string or nil
    --                       1      -- formatted date
    --                       align  -- c l r
    --                       class
    --                       id
    --                       style
    --                       data-sort-type
    --    advanced  -- table, invoke parameters
    --                       cat  -- category
    -- Returns:
    --     string, as template result; leading cell attributes
    local show = args[ 1 ]  or  ""
    local data = fetch( show )
    local sort
    if data then
        sort = args[ "data-sort-type" ]  or  "text"
        if sort == "text" then
            sort = figure( data, false, true )
        elseif sort == "number" then
            sort = figure( data, false, false )
        elseif sort == "isoDate" then
            sort = data:format( "ISO" )
        elseif sort == "date" then
            sort = data:format( "data-sort-type:date" )
        else
            sort = figure( data, false, true )
        end
    else
        sort = Invalid
        show = show .. fault( "(?!)" )
        if advanced.cat then
            show = string.format( "%s[[Category:%s]]",
                                  show, advanced.cat )
        end
    end
    return field( sort, show, args )
end -- Templates.SD()



local function dts_dtsx( assigned, allow, append, appoint, alone )
    -- Output of Templates:dts Templates:dtsx
    -- Parameter:
    --    assigned  -- table, date object
    --    allow     -- any: permit break before year
    --    append    -- string with additional sorting information, or nil
    --    appoint   -- string with additional text to be shown, or nil
    --    alone     -- permit hour without minute
    local opts = { london = true }
    local show, sort, spec
    if favorite( allow ) then
        spec = "T._Mon4 JJJJ hh:mm:ss"
    else
        spec = "T._Mon4_JJJJ hh:mm:ss"
    end
    if alone then
        opts.lonely = true
    end
    show = assigned:format( spec, opts )
    if assigned.dom then
        if assigned.dom < 10 then
            show = Span0 .. show
        end
    end
    if appoint then
        show = string.format( "%s&#160;%s", show, appoint )
    end
    sort = figure( assigned, false, true )
    if append then
        sort = string.format( "%s %s", sort, append )
    end
    return  flag( sort, show )
end -- dts_dtsx()



Templates.dts = function ( args )
    -- Support Template:dts
    -- Parameter:
    --    args   -- table, template parameters; each string or nil
    --                     1  -- dom
    --                     2  -- month, name, abbreviated name or number
    --                     3  -- year, since 1
    --                     4  -- time hh:mm:ss
    --                     u  -- any: permit break before year
    -- Returns:
    --     string, as template result; leading sortkey
    local r    = args[ 4 ]
    local live = r
    local data
    if not r then
        r = false
    end
    data = fetch( r )
    if data then
        if args[ 1 ] then
            data.dom = tonumber( args[ 1 ] )
            live     = true
        end
        r = args[ 2 ]
        if r then
            r = mw.text.trim( r )
            if r:match( "^%d+$" ) then
                data.month = tonumber( r )
                live       = true
            else
                data.lang = "de"
                r         = data:figure( r )
                if r then
                    live = true
                end
            end
        end
        r = args[ 3 ]
        if r and r:match( "^%s*-?%d+%s*$" ) then
            r = tonumber( r )
            if r < 0 then
                data.bc = true
                r       = - r
            end
            data.year = r
            live      = true
        end
    else
        live = false
    end
    if live then
        r = dts_dtsx( data, args.u )
    else
        r = ""
    end
    return r
end -- Templates.dts()



Templates.dtsx = function ( args )
    -- Support Template:dtsx
    -- Parameter:
    --    args   -- table, template parameters; each string or nil
    --                     1      -- dom
    --                     2      -- month, number
    --                     3      -- year, since 1
    --                     4      -- hours
    --                     5      -- minutes
    --                     6      -- seconds
    --                     extra  -- additional sorting information
    --                     u      -- any: permit break before year
    -- Returns:
    --     string, as template result; leading sortkey
    local data = fetch( false )
    local r    = args[ 2 ]
    local lonely
    local suffix
    if r then
        if r:match( "^%s*%d+%s*$" ) then
            data.month = tonumber( r )
            live       = true
            r          = args[ 1 ]
            if r and r:match( "^%s*%d+%s*$" ) then
                data.dom = tonumber( r )
            end
        end
        r = args[ 3 ]
        if r and r:match( "^%s*%-?%d+%s*$" ) then
            r = tonumber( r )
            if r < 0 then
                data.bc = true
                r       = - r
            end
            data.year = r
            live      = true
        end
    end
    r = args[ 4 ]
    if r then
        if r:match( "^%s*%d+%s*$" ) then
            data.hour = tonumber( r )
            suffix    = "Uhr"
            live      = true
            r         = args[ 5 ]
            if r and r:match( "^%s*%d+%s*$" ) then
                data.min = tonumber( r )
                r = args[ 6 ]
                if r and r:match( "^%s*%d+%s*$" ) then
                    data.sec = tonumber( r )
                end
            else
                lonely = true
            end
        end
    end
    if live then
        r = dts_dtsx( data, args.u, args.extra, suffix, lonely )
    else
        r = ""
    end
    return r
end -- Templates.dtsx()



Templates.SortDate = function ( args )
    -- Support Template:dts
    -- Parameter:
    --    args   -- table, template parameters; string or nil
    --                     1          -- ISO-date, BC with leading hyphen
    --                     2          -- F  -- full month name
    --                                   M  -- 3-letter-abbr month
    --                                   S  -- 4 letter or abbr month
    --                     2 or 3     -- nbsp  -- no line break at year
    --                                   sp    -- permit break at year
    --                     2, 3 or 4  -- link date
    --                     AT         -- Austrian, if not empty
    --                     davor      -- leading string displayed
    -- Returns:
    --     string, as template result; leading sortkey
    local show  = args[ 1 ]
    local sort  = Invalid
    local start = args.davor
    if show then
        show = mw.text.trim( show )
        if show ~= "" then
            local data  = fetch( false )
            local live  = false
            local y, m, d
            if ( mw.ustring.codepoint( show, 1, 1 ) == 45 ) then
                show = show:sub( 2 )
                data.bc = true
            end
            y, m, d = show:match( "^(%-?%d+)%-(%d+)%-(%d+)$" )
            if y then
                m = tonumber( m )
                d = tonumber( d )
                if m > 0 then
                   data.month = m
                   live       = true
                end
                if d > 0 then
                   data.dom = d
                   live     = true
                end
            else
                y = show:match( "^(%-?%d+)$" )
            end
            if y then
                y = tonumber( y )
                if y < 0 then
                    data.bc = true
                    y       = - y
                end
                if y > 0 then
                   data.year = y
                   live      = true
                end
            end
            if live then
                local key  = 2
                local got  = args[ key ]
                local mode = 0
                local lines, link
                if got then
                    if got == "M" then
                        mode = 3
                    elseif got == "S" then
                        mode = 4
                    elseif got ~= "F" then
                        got = false
                    end
                    if got then
                        key = key + 1
                    end
                    got = args[ key ]
                    if got then
                        if got == "sp" then
                            lines = true
                        elseif got ~= "nbsp" then
                            got = false
                        end
                        if got then
                            key = key + 1
                        end
                        link = ( args[ key ] == "link" )
                    end
                end
                if data.month then
                    local lAT1  = ( data.month == 1 and
                                    favorite( args.AT ) )
                    local score = data:full()
                    if mode == 0 then
                        if lAT1 then
                            show = "Jänner"
                        else
                            show = score
                        end
                    elseif mw.ustring.len( score ) > mode then
                        if lAT1 then
                            show = "Jän."
                        else
                            show = data:first()
                        end
                    else
                        show = score
                        mode = 0
                    end
                    if data.dom then
                        local lazy = ( link  and mode == 0 )
                        show = string.format( "%d.&#160;%s",
                                              data.dom, show )
                        if link then
                            if lazy then
                                show = string.format( "[[%s]]", show )
                            else
                                show = string.format( "[[%d. %s|%s]]",
                                                      data.dom, score,
                                                      show )
                            end
                        end
                        if data.dom < 10 then
                            show = Span0 .. show
                        end
                    end
                    if data.year then
                        if lines then
                            show = show .. " "
                        else
                            show = show .. "&#160;"
                        end
                    end
                else
                    show = ""
                end
                if data.year then
                    local s = tostring( data.year )
                    if link then
                        show = string.format( "%s[[%s]]", show, s )
                    else
                        show = show .. s
                    end
                    if data.bc then
                        show = show .. "&#160;v.&#8239;Chr."
                    end
                end
                sort = figure( data, false, true )
            end
        end
    else
        show = ""
    end
    if start then
        if #start > 0 then
            if #show > 0 then
                show = " " .. show
            end
            show = start .. show
        end
    end
    return flag( sort, show )
end -- Templates.SortDate()



-- Export
local p = { }

function p.f( frame )
    local l, r = pcall( fruit,
                        mw.text.trim( frame.args[ 1 ] or "" ),
                        mw.text.trim( frame.args[ 2 ] or "de" ) )
    return r
end -- p.f

function p.fruit( analyse, alien )
    local l, r = pcall( fruit, analyse, alien )
    return r
end -- p.fruit

function p.template( frame )
    local r
    local fun = frame.args[ 1 ]
    if fun and Templates[ fun ] then
        local l
        l, r = pcall( Templates[ fun ],
                      frame:getParent().args,
                      frame.args )
    else
        r = "invalid template"
    end
    return r
end -- p.template

function p.test( apply, args, adjust )
    local r
    if apply and Templates[ apply ] then
        local l
        l, r = pcall( Templates[ apply ], args, adjust )
    end
    return r
end -- p.test

return p