Modul:Vorlage:Anker: Unterschied zwischen den Versionen

Aus Regiowiki
Zur Navigation springen Zur Suche springen
(2019-12-24)
K (13 Versionen importiert: Doku-Vorlage)
 
(9 dazwischenliegende Versionen von einem anderen Benutzer werden nicht angezeigt)
Zeile 1: Zeile 1:
local Anchor = { suite  = "AnchorTemplate",
local Anchors = { suite  = "Anchors",
                serial  = "2019-12-24",
                  serial  = "2020-04-10",
                item    = 79414611
                  item    = 79414611,
              }
                  globals = { Multilingual = 47541920,
                              TemplUtl    = 52364930 }
                }
--[=[
--[=[
Template:Anchor  {{Anker}}
Support Template:Anchor  {{Anker}}
]=]
]=]
local Failsafe = Anchor
local Failsafe = Anchors
local GlobalMod = Anchors






local Config = {
local Config = {
     badPattern    = "[%%#'\"%[%]<>]",
     badPattern    = "[#'\"%[%]<>|]",
     globalPage    = { "bodyContent",
     globalPage    = { "antispam-container",
                      "bodyContent",
                       "catlinks",
                       "catlinks",
                       "centralNotice",
                       "centralNotice",
                       "content",
                       "content",
                       "contentSub",
                       "contentSub",
                      "contentSub2",
                       "editform",
                       "editform",
                       "editpage-copywarn",
                       "editpage-copywarn",
Zeile 23: Zeile 28:
                       "fundraising",
                       "fundraising",
                       "jump-to-nav",
                       "jump-to-nav",
                      "language-settings-dialog",
                      "languagesettings-panels",
                       "page-actions",
                       "page-actions",
                       "siteNotice",
                       "siteNotice",
                       "siteSub",
                       "siteSub",
                       "toc",
                       "toc",
                       "top" },
                      "toctitle",
                       "top",
                      "wikiPreview" },
     globalPatterns = { "^mw%-[%l%-]+$",
     globalPatterns = { "^mw%-[%l%-]+$",
                       "^ca%-[%l%-]+$",
                       "^ca%-[%l%-]+$",
                       "^n%-[%l%-]+$",
                       "^n%-[%l%-]+$",
                       "^pt?%-[%l%-]+$",
                      "^p%-%a%a[%a%-]+$",
                       "^pt%-[%l%-]+$",
                       "^wp%u" },
                       "^wp%u" },
     errCat        = false,
     errCat        = false,
Zeile 46: Zeile 56:
                       de = "Parameter unbekannt:" }
                       de = "Parameter unbekannt:" }
}
}
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
    -- 2020-01-01
    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 )
        end
    end
    return r
end -- foreignModule()




Zeile 53: Zeile 116:
     --    apply  -- string, with message key
     --    apply  -- string, with message key
     -- Returns message text; at least English
     -- Returns message text; at least English
    -- TODO: Might be extended by tabData and Multilingual
     local entry = Config[ apply ]
     local entry = Config[ apply ]
     local r
     local r
Zeile 76: Zeile 140:
     --    adjust  -- string or nil
     --    adjust  -- string or nil
     -- Returns boolean
     -- Returns boolean
     local r = false
     local r
     if adjust then
     if type( adjust ) == "string" then
         r = mw.text.trim( adjust )
         r = mw.text.trim( adjust )
         if r ~= ""  and  r ~= "0" then
         if r ~= ""  and  r ~= "0" then
             r = true
             r = true
         end
         end
    else
        r = adjust or false
     end
     end
     return r
     return r
Zeile 90: Zeile 156:
local function fair( assembly )
local function fair( assembly )
     -- Create HTML code for anchors
     -- Create HTML code for anchors
     --    assembly  -- sequence table, with identtifiers
     --    assembly  -- sequence table, with identifiers
     -- Returns HTML string
     -- Returns HTML string
     local collection = mw.html.create( "" )
     local collection = mw.html.create( "" )
Zeile 124: Zeile 190:
     local scope = Config.errClass
     local scope = Config.errClass
     local story = factory( alert )
     local story = factory( alert )
     local lucky, TemplUtl = pcall( require, "Module:TemplUtl" )
     local TemplUtl = foreignModule( "TemplUtl",
                                    true,
                                    false,
                                    Anchors.globals.TemplUtl )
     local r, scope, style
     local r, scope, style
     if type( TemplUtl ) == "table" then
     if type( TemplUtl ) == "table"  and
      type( TemplUtl.TemplUtl ) == "function" then
         TemplUtl = TemplUtl.TemplUtl()
         TemplUtl = TemplUtl.TemplUtl()
    else
        TemplUtl = false
     end
     end
     if Config.self then
     if Config.self then
Zeile 143: Zeile 215:
         story = string.format( "%s %s", story, about )
         story = string.format( "%s %s", story, about )
     end
     end
     if type( TemplUtl ) == "table" then
     if TemplUtl then
         r = TemplUtl.failure( story,
         r = TemplUtl.failure( story,
                               not Config.errHide,
                               not Config.errHide,
Zeile 183: Zeile 255:
     return r
     return r
end -- fault()
end -- fault()
local function features( adapt )
    -- Set parameters on compliance
    if type( adapt ) == "table" then
        Config.limitMarkup = faculty( adapt.rejectMarkup )
        Config.limitMW    = faculty( adapt.rejectMW )
        Config.limitXML    = faculty( adapt.onlyXML )
        Config.lightXML    = faculty( adapt.softXML )
    end
end -- features()




Zeile 192: Zeile 276:
     -- Returns appropriate string
     -- Returns appropriate string
     local r
     local r
     if argsF then
     if type( argsF ) == "table" then
         Config.errCat    = argsF.errCat
         Config.errCat    = argsF.errCat
         Config.errClasses = argsF.errClasses
         Config.errClasses = argsF.errClasses
         Config.errHide    = faculty( argsF.errHide )
         Config.errHide    = faculty( argsF.errHide )
         Config.errNS      = argsF.errNS
         Config.errNS      = argsF.errNS
if mw.site.server:find( "de.wikipedia.org", 1, true ) then
        features( argsF )
     Config.errClasses = "Linkwartung"
if mw.site.server:find( "de.wikipedia.", 1, true ) then
     Config.errHide = true
     Config.errClasses = "Linkwartung"
     Config.errNS = 0
     Config.errHide     = true
     Config.errNS       = 0
    Config.limitMarkup = true
    Config.limitMW    = false
    Config.limitXML    = true
    Config.lightXML    = true
end
end
     end
     end
     if type( argsT ) == "table" then
     if type( argsT ) == "table" then
         local checked = { }
         local e, got, less, s, unknown
        local tocheck = { }
        local further = function ( add )
                            local s = mw.text.unstripNoWiki( add )
                            s = s:gsub( " +", "_" )
                                :gsub( "_+", "_" )
                            table.insert( checked, s )
                        end
        local s, unknown
         for k, v in pairs( argsT ) do
         for k, v in pairs( argsT ) do
             s = type( k )
             if type( k ) == "number" then
            if s == "number" then
                 v = mw.text.trim( v )
                 v = mw.text.trim( v )
                 if v ~= "" then
                 if v == "" then
                     table.insert( tocheck, v )
                     v = false
                 end
                 end
                less = ( k < 0 )
                 k = false
                 k = false
             elseif s == "string" then
             else
                 if k:match( "^x[1-9]%d*" ) then
                 less = k:match( "^[x%-][1-9]%d*" )
                    further( v )
                if less then
                     k = false
                     k = false
                 end
                 end
             end
             end
             if k then
             if k then
                 if not unknown then
                 unknown = unknown or  { }
                    unknown = { }
                end
                 table.insert( unknown, k )
                 table.insert( unknown, k )
            elseif v then
                got      = got  or  { }
                v        = v:gsub( " +", "_" )
                            :gsub( "_+", "_" )
                got[ v ] = less
             end
             end
         end -- for k, v
         end -- for k, v
         if unknown then
         if unknown then
             local e = mw.html.create( "code" )
             e = mw.html.create( "code" )
                        :css( "white-space", "nowrap" )
                  :css( "white-space", "nowrap" )
                        :wikitext( table.concat( unknown, " " ) )
                  :wikitext( table.concat( unknown, " " ) )
             s = string.format( " in [[%s]]",
             s = string.format( " in [[%s]]",
                               Config.frame:getTitle() )
                               Config.frame:getTitle() )
Zeile 243: Zeile 327:
             :wikitext( s )
             :wikitext( s )
             r = fault( "errUnkown", tostring( e ) )
             r = fault( "errUnkown", tostring( e ) )
         else
         elseif got then
             local bad, e
             local profile = { limitMarkup = false,
            local flop = function ( a, alter )
                              limitMW    = false,
                            if not bad then
                              limitXML    = false }
                                bad = { }
            local bad, checked, legal, o, s
                            end
             for k, v in pairs( got ) do
                            e = mw.html.create( "code" )
                 if v then
                                        :wikitext( a )
                     o = profile
                            if alter then
                                e:css( "white-space", "nowrap" )
                            end
                            table.insert( bad, tostring( e ) )
                        end
            local legal, s
             for i = 1, #tocheck do
                s = tocheck[ i ]
                 if s:match( "^%d" )  or
                  s:match( Config.badPattern )  or
                  s:match( "&#%a+;" ) then
                     legal = false
                 else
                 else
                     legal = true
                     o = false
                    for k = 1, #Config.globalPatterns do
                end
                        if s:match( Config.globalPatterns[ k ] ) then
                s, legal = Anchors.feasible( k, o )
                            legal = false
                if s then
                            break    -- k = 1, #Config.globalPatterns
                    checked = checked  or  { }
                        end
                    table.insert( checked, s )
                    end -- k = 1, #Config.globalPatterns
                 end
                 end
                 if not legal then
                 if not legal then
                     flop( mw.text.encode( s ),  true )
                     bad = bad  or  { }
                else
                    e  = mw.html.create( "code" )
                     further( s )
                                :css( "white-space", "nowrap" )
                                :wikitext( mw.text.encode( k ) )
                     table.insert( bad, tostring( e ) )
                 end
                 end
             end -- i = 1, #tocheck
             end -- for k, v
             for i = 1, #checked do
             if checked then
                 s = checked[ i ]
                 r = fair( checked )
                for k = 1, #Config.globalPage do
             end
                    if s == Config.globalPage[ k ] then
                        flop( s )
                        break    -- k = 1, #Config.globalPage
                    end
                end -- k = 1, #Config.globalPage
             end -- i = 1, #checked
             if bad then
             if bad then
                 s = string.format( "%s in [[%s]]",
                 s = string.format( "%s in [[%s]]",
                                   table.concat( bad, ", " ),
                                   table.concat( bad, ", " ),
                                   Config.frame:getParent():getTitle() )
                                   Config.frame:getParent():getTitle() )
                 r = fault( "errInvalid", s )
                 r = string.format( "%s%s",
            elseif #checked == 0 then
                                  r or "",
                r = fault( "errNoFragments" )
                                  fault( "errInvalid", s ) )
            else
                r = fair( checked )
             end
             end
        else
            r = fault( "errNoFragments" )
         end
         end
     end
     end
     return r or ""
     return r or ""
end -- main()
end -- main()
Anchors.feasible = function ( analyse, allow )
    -- Check ID for compliance
    -- Precondition:
    --    analyse  -- string, with presumptive anchor
    --    allow    -- optional table, with compliance criteria
    --                  -- limitMarkup
    --                  -- limitMW
    --                  -- limitXML
    --                  -- lightXML
    -- Postcondition:
    --    Returns
    --        1.  -- string  -- with normalised anchor
    --              -- false  -- if not appropriate
    --        2.  -- true, if fully compliant
    local r1 = false
    local r2 = false
    if type( analyse ) == "string" then
        local s = mw.text.trim( analyse )
        if s ~= "" then
            local deny = { limitMarkup = Config.limitMarkup,
                          limitMW    = Config.limitMW,
                          limitXML    = Config.limitXML,
                          lightXML    = Config.lightXML }
            if type( allow ) == "table" then
                for k, v in pairs( deny ) do
                    if type( allow[ k ] ) == "boolean" then
                        deny[ k ] = allow[ k ]
                    end
                end -- for k, v
            end
            if deny.limitMW then
                r1 = true
            else
                s  = mw.text.unstripNoWiki( s )
                s  = mw.text.trim( s )
                r1 = ( s ~= "" )
            end
            if r1  and  deny.limitMarkup  and
              ( s:match( Config.badPattern )  or
                s:match( "&#%w+;" ) ) then
                r1 = false
            end
            if r1 then
                for k = 1, #Config.globalPage do
                    if s == Config.globalPage[ k ] then
                        r1 = false
                        break    -- k = 1, #Config.globalPage
                    end
                end -- k = 1, #Config.globalPage
                if r1 then
                    Config.badCtrl = Config.badCtrl  or
                                    string.format( "[%c-%c]", 1, 31 )
                    r1 = not s:find( Config.badCtrl )
                    if r1  and  s:match( "%%%x%x" ) then
                        r1 = false
                    end
                    if r1 then
                        for k = 1, #Config.globalPatterns do
                            if s:match( Config.globalPatterns[ k ] ) then
                                r1 = false
                                break    -- k = 1, #Config.globalPatterns
                            end
                        end -- k = 1, #Config.globalPatterns
                    end
                end
            end
            if r1 then
                r2 = true
                if deny.limitXML  and
                  ( s:match( "^%d" )  or
                    s:sub( 1, 1 ) == "-" ) then
                    r2 = false
                    if not deny.lightXML then
                        r1 = false
                    end
                end
            end
            if r1 then
                r1 = s:gsub( " +", "_" )
                      :gsub( "_+", "_" )
            end
        end
    end
    return r1, r2
end -- Anchors.feasible()




Zeile 366: Zeile 521:
end -- p.f()
end -- p.f()


p.flops = function ( frame )
p.feasible = function ( frame )
    local r1, r2
    Config.frame = frame
    features( frame.args )
    r1, r2 = Anchors.feasible( frame.args[ 1 ] )
    return r1 or ""
end -- p.feasible()
 
p.forbidden = function ()
     local r  = ""
     local r  = ""
     local sep = ""
     local sep = ""
Zeile 378: Zeile 541:
     end -- k = 1, #Config.globalPage
     end -- k = 1, #Config.globalPage
     return r
     return r
end -- p.flops()
end -- p.forbidden()


p.failsafe = function ( frame )
p.failsafe = function ( frame )
Zeile 397: Zeile 560:
     return Failsafe.failsafe( since )  or  ""
     return Failsafe.failsafe( since )  or  ""
end -- p.failsafe()
end -- p.failsafe()
p.Anchors = function ()
    return Anchors
end -- p.Anchors


return p
return p

Aktuelle Version vom 3. Juli 2020, 22:19 Uhr

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

local Anchors = { suite   = "Anchors",
                  serial  = "2020-04-10",
                  item    = 79414611,
                  globals = { Multilingual = 47541920,
                              TemplUtl     = 52364930 }
                }
--[=[
Support Template:Anchor   {{Anker}}
]=]
local Failsafe  = Anchors
local GlobalMod = Anchors



local Config = {
    badPattern     = "[#'\"%[%]<>|]",
    globalPage     = { "antispam-container",
                       "bodyContent",
                       "catlinks",
                       "centralNotice",
                       "content",
                       "contentSub",
                       "contentSub2",
                       "editform",
                       "editpage-copywarn",
                       "firstHeading",
                       "footer",
                       "fundraising",
                       "jump-to-nav",
                       "language-settings-dialog",
                       "languagesettings-panels",
                       "page-actions",
                       "siteNotice",
                       "siteSub",
                       "toc",
                       "toctitle",
                       "top",
                       "wikiPreview" },
    globalPatterns = { "^mw%-[%l%-]+$",
                       "^ca%-[%l%-]+$",
                       "^n%-[%l%-]+$",
                       "^p%-%a%a[%a%-]+$",
                       "^pt%-[%l%-]+$",
                       "^wp%u" },
    errCat         = false,
    errClass       = "error_fragment",
    errHide        = true,
    errNS          = false,
    errInvalid     = { en = "Invalid:",
                       de = "Ungültig:" },
    errModule      = { en = "Library module missing:",
                       de = "Bibliotheksmodul fehlt:" },
    errNoFragments = { en = "No fragments given",
                       de = "Keine Bezeichner angegeben" },
    errUnkown      = { en = "Unkown parameter:",
                       de = "Parameter unbekannt:" }
}



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
    -- 2020-01-01
    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 )
        end
    end
    return r
end -- foreignModule()



local function factory( apply )
    -- Localization of messages
    --     apply  -- string, with message key
    -- Returns message text; at least English
    -- TODO: Might be extended by tabData and Multilingual
    local entry = Config[ apply ]
    local r
    if entry then
        -- TODO: page language
        r = entry[ mw.language.getContentLanguage():getCode() ]
        if not r then
            r = entry.en
        end
    else
        r = tostring( mw.html.create( "span" )
                        :addClass( "error" )
                        :wikitext( string.format( "????.%s.????",
                                                  apply ) ) )
    end
    return r
end -- factory()



local function faculty( adjust )
    -- Test template arg for boolean
    --     adjust  -- string or nil
    -- Returns boolean
    local r
    if type( adjust ) == "string" then
        r = mw.text.trim( adjust )
        if r ~= ""  and  r ~= "0" then
            r = true
        end
    else
        r = adjust or false
    end
    return r
end -- faculty()



local function fair( assembly )
    -- Create HTML code for anchors
    --     assembly  -- sequence table, with identifiers
    -- Returns HTML string
    local collection = mw.html.create( "" )
    local f = function ( a )
                  collection:node( mw.html.create( "span" )
                                          :attr( "id", a ) )
              end
    local s, seen, shift
    table.sort( assembly )
    for i = 1, #assembly do
        s = assembly[ i ]
        if s ~= seen then
            if not s:match( "^[%w_]+$" ) then
                shift = mw.uri.encode( s, "WIKI" )
                if shift ~= s then
                    f( shift:gsub( "%%", "." ) )
                end
            end
            f( s )
            seen = s
        end
    end -- i = 1, #assembly
    return tostring( collection )
end -- fair()



local function fault( alert, about )
    -- Format message with class="error" or similar
    --     alert  -- string, with message key
    --     about  -- string, with explanation
    -- Returns message with markup
    local scope = Config.errClass
    local story = factory( alert )
    local TemplUtl = foreignModule( "TemplUtl",
                                    true,
                                    false,
                                    Anchors.globals.TemplUtl )
    local r, scope, style
    if type( TemplUtl ) == "table"  and
       type( TemplUtl.TemplUtl ) == "function" then
        TemplUtl = TemplUtl.TemplUtl()
    else
        TemplUtl = false
    end
    if Config.self then
        story = string.format( "%s * %s", Config.self, story )
    end
    if Config.errClasses then
        if scope then
            scope = string.format( "%s %s",
                                   scope, Config.errClasses )
        else
            scope = Config.errClasses
        end
    end
    if about then
        story = string.format( "%s %s", story, about )
    end
    if TemplUtl then
        r = TemplUtl.failure( story,
                              not Config.errHide,
                              scope,
                              Config.frame )
    else
        r = tostring( mw.html.create( "span" )
                             :addClass( scope )
                             :addClass( "error" )
                             :wikitext( story ) )
    end
    if Config.errCat then
        if Config.errNS then
            local st = type( Config.errNS )
            if st == "number" then
                st = { }
                table.insert( st, Config.errNS )
                Config.errNS = st
            elseif st == "string" then
                Config.errNS = mw.text.split( Config.errNS, "%s+" )
                for i = 1, #Config.errNS do
                    Config.errNS[ i ] = tonumber( Config.errNS[ i ] )
                end -- for i
            end
            if type( Config.errNS ) == "table" then
                local ns = mw.title.getCurrentTitle().namespace
                for i = 1, #Config.errNS do
                    if Config.errNS[ i ] == ns then
                        Config.errNS = false
                        break    -- for i
                    end
                end -- for i
            end
        end
        if not Config.errNS then
            r = string.format( "%s[[Category:%s]]", r, Config.errCat )
        end
    end
    return r
end -- fault()



local function features( adapt )
    -- Set parameters on compliance
    if type( adapt ) == "table" then
        Config.limitMarkup = faculty( adapt.rejectMarkup )
        Config.limitMW     = faculty( adapt.rejectMW )
        Config.limitXML    = faculty( adapt.onlyXML )
        Config.lightXML    = faculty( adapt.softXML )
    end
end -- features()



local function main( argsF, argsT )
    -- Invocation
    --     argsF  -- table, with #invoke parameters, or false
    --     argsT  -- table, with template parameters
    -- Returns appropriate string
    local r
    if type( argsF ) == "table" then
        Config.errCat     = argsF.errCat
        Config.errClasses = argsF.errClasses
        Config.errHide    = faculty( argsF.errHide )
        Config.errNS      = argsF.errNS
        features( argsF )
if mw.site.server:find( "de.wikipedia.", 1, true ) then
    Config.errClasses  = "Linkwartung"
    Config.errHide     = true
    Config.errNS       = 0
    Config.limitMarkup = true
    Config.limitMW     = false
    Config.limitXML    = true
    Config.lightXML    = true
end
    end
    if type( argsT ) == "table" then
        local e, got, less, s, unknown
        for k, v in pairs( argsT ) do
            if type( k ) == "number" then
                v = mw.text.trim( v )
                if v == "" then
                    v = false
                end
                less = ( k < 0 )
                k = false
            else
                less = k:match( "^[x%-][1-9]%d*" )
                if less then
                    k = false
                end
            end
            if k then
                unknown = unknown  or  { }
                table.insert( unknown, k )
            elseif v then
                got      = got  or  { }
                v        = v:gsub( " +", "_" )
                            :gsub( "_+", "_" )
                got[ v ] = less
            end
        end -- for k, v
        if unknown then
            e = mw.html.create( "code" )
                  :css( "white-space", "nowrap" )
                  :wikitext( table.concat( unknown, " " ) )
            s = string.format( " in [[%s]]",
                               Config.frame:getTitle() )
            e:tag( "span" )
             :wikitext( s )
            r = fault( "errUnkown", tostring( e ) )
        elseif got then
            local profile = { limitMarkup = false,
                              limitMW     = false,
                              limitXML    = false }
            local bad, checked, legal, o, s
            for k, v in pairs( got ) do
                if v then
                    o = profile
                else
                    o = false
                end
                s, legal = Anchors.feasible( k, o )
                if s then
                    checked = checked  or  { }
                    table.insert( checked, s )
                end
                if not legal then
                    bad = bad  or  { }
                    e   = mw.html.create( "code" )
                                 :css( "white-space", "nowrap" )
                                 :wikitext( mw.text.encode( k ) )
                    table.insert( bad, tostring( e ) )
                end
            end -- for k, v
            if checked then
                r = fair( checked )
            end
            if bad then
                s = string.format( "%s in [[%s]]",
                                   table.concat( bad, ", " ),
                                   Config.frame:getParent():getTitle() )
                r = string.format( "%s%s",
                                   r or "",
                                   fault( "errInvalid", s ) )
            end
        else
            r = fault( "errNoFragments" )
        end
    end
    return r or ""
end -- main()



Anchors.feasible = function ( analyse, allow )
    -- Check ID for compliance
    -- Precondition:
    --     analyse   -- string, with presumptive anchor
    --     allow     -- optional table, with compliance criteria
    --                  -- limitMarkup
    --                  -- limitMW
    --                  -- limitXML
    --                  -- lightXML
    -- Postcondition:
    --     Returns
    --         1.   -- string  -- with normalised anchor
    --              -- false   -- if not appropriate
    --         2.   -- true, if fully compliant
    local r1 = false
    local r2 = false
    if type( analyse ) == "string" then
        local s = mw.text.trim( analyse )
        if s ~= "" then
            local deny = { limitMarkup = Config.limitMarkup,
                           limitMW     = Config.limitMW,
                           limitXML    = Config.limitXML,
                           lightXML    = Config.lightXML }
            if type( allow ) == "table" then
                for k, v in pairs( deny ) do
                    if type( allow[ k ] ) == "boolean" then
                        deny[ k ] = allow[ k ]
                    end
                end -- for k, v
            end
            if deny.limitMW then
                r1 = true
            else
                s  = mw.text.unstripNoWiki( s )
                s  = mw.text.trim( s )
                r1 = ( s ~= "" )
            end
            if r1   and   deny.limitMarkup   and
               ( s:match( Config.badPattern )  or
                 s:match( "&#%w+;" ) ) then
                r1 = false
            end
            if r1 then
                for k = 1, #Config.globalPage do
                    if s == Config.globalPage[ k ] then
                        r1 = false
                        break    -- k = 1, #Config.globalPage
                    end
                end -- k = 1, #Config.globalPage
                if r1 then
                    Config.badCtrl = Config.badCtrl  or
                                     string.format( "[%c-%c]", 1, 31 )
                    r1 = not s:find( Config.badCtrl )
                    if r1  and  s:match( "%%%x%x" ) then
                        r1 = false
                    end
                    if r1 then
                        for k = 1, #Config.globalPatterns do
                            if s:match( Config.globalPatterns[ k ] ) then
                                r1 = false
                                break    -- k = 1, #Config.globalPatterns
                            end
                        end -- k = 1, #Config.globalPatterns
                    end
                end
            end
            if r1 then
                r2 = true
                if deny.limitXML   and
                   ( s:match( "^%d" )  or
                     s:sub( 1, 1 ) == "-" ) then
                    r2 = false
                    if not deny.lightXML then
                        r1 = false
                    end
                end
            end
            if r1 then
                r1 = s:gsub( " +", "_" )
                      :gsub( "_+", "_" )
            end
        end
    end
    return r1, r2
end -- Anchors.feasible()



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 )
    local lucky, r
    Config.frame = frame
    lucky, r = pcall( main, frame.args, frame:getParent().args )
    if not lucky then
        r = tostring( mw.html.create( "span" )
                        :addClass( "error" )
                        :wikitext( string.format( "%s * %s",
                                                  frame:getTitle(),
                                                  r ) ) )
    end
    return r
end -- p.f()

p.feasible = function ( frame )
    local r1, r2
    Config.frame = frame
    features( frame.args )
    r1, r2 = Anchors.feasible( frame.args[ 1 ] )
    return r1 or ""
end -- p.feasible()

p.forbidden = function ()
    local r   = ""
    local sep = ""
    local e
    for k = 1, #Config.globalPage do
        e   = mw.html.create( "code" )
                     :css( "white-space", "nowrap" )
                     :wikitext( Config.globalPage[ k ] )
        r   = string.format( "%s%s%s", r, sep, tostring( e ) )
        sep = ", "
    end -- k = 1, #Config.globalPage
    return r
end -- p.forbidden()

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.Anchors = function ()
    return Anchors
end -- p.Anchors

return p