Modul:MediaWikiGadgetDefinition

Vorlagenprogrammierung Diskussionen Lua 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-11-15

local MediaWikiGadgetDefinition = { suite  = "MediaWikiGadgetDefinition",
                                    serial = "2024-11-15",
                                    item   = 111520596 }
--[==[
Documentation, validation and comparison of MediaWiki gadget definitions
]==]
local Failsafe     = MediaWikiGadgetDefinition
local GlobalMod    = MediaWikiGadgetDefinition



local ACTIVE   = { }
local COMMONS  = { }
local ERROR    = { }
local GLOBAL   = { }
local HTML     = { bgcolorSleeping = "D0D0D0" }
local I18N     = { }
local OPTIONS  = { }
local ROW      = { comment = { live   = false,
                               suffix = "#" } }
local TEMPLATE = { }
local VALUES   = { }



COMMONS.tabs =    { config        = "MediaWikiGadgetDefinition",
                    actions       = "MediaWiki/URLactions",
                    contentModels = "MediaWiki/Contentmodels",
                    skins         = "MediaWiki/SkinsWMF",
                    modules       = "MediaWiki/ResourceLoaderModules"
                  }
OPTIONS.config  = { { sign = "ID",
                      live = true,
                      mode = 0,
                      type = "string",
                      scan = "rootpage" },
                    { sign = "ResourceLoader",
                      live = true,
                      mode = 1,
                      type = "boolean" },
                    { sign = "default",
                      live = true,
                      mode = 1,
                      type = "boolean" },
                    { sign = "hidden",
                      live = true,
                      mode = 1,
                      type = "boolean" },
                    { sign = "package",
                      live = true,
                      mode = 1,
                      type = "boolean" },
                    { sign = "targets",
                      live = true,
                      mode = 1,
                      type = { "string" },
                      scan = "array" },
                    { sign = "skins",
                      live = true,
                      mode = 1,
                      type = { "string" },
                      scan = "array" },
                    { sign = "rights",
                      live = true,
                      mode = 1,
                      type = { "string" },
                      scan = "letter-id" },
                    { sign = "actions",
                      live = true,
                      mode = 1,
                      type = { "string" },
                      scan = "array" },
                    { sign = "namespaces",
                      live = true,
                      mode = 1,
                      type = { "number" },
                      scan = "namespace" },
                    { sign = "specials",
                      live = false,
                      mode = 1,
                      type = { "string" },
                      scan = "LetterID" },
                    { sign = "contentModels",
                      live = true,
                      mode = 1,
                      type = { "string" },
                      scan = "array" },
                    { sign = "pageprops",
                      live = false,
                      mode = 1,
                      type = { "string" },
                      scan = "letter-_ID" },
                    { sign = "transcludes",
                      live = false,
                      mode = 1,
                      type = { "string" },
                      scan = "page" },
                    { sign = "groups",
                      live = false,
                      mode = 1,
                      type = { "string" },
                      scan = "letter-id" },
                    { sign = "userlangs",
                      live = false,
                      mode = 1,
                      type = { "string" },
                      scan = "lang" },
                    { sign = "contentlangs",
                      live = false,
                      mode = 1,
                      type = { "string" },
                      scan = "lang" },
                    { sign = "category",
                      live = false,
                      mode = 1,
                      type = "string",
                      scan = "page" },
                    { sign = "categories",
                      live = true,
                      mode = 1,
                      type = { "string" },
                      scan = "page" },
                    { sign = "type",
                      live = true,
                      mode = 1,
                      type = "string",
                      scan = "radio" },
                    { sign = "top",
                      live = true,
                      mode = 1,
                      type = "boolean" },
                    { sign = "supportsUrlLoad",
                      live = true,
                      mode = 1,
                      type = "boolean" },
                    { sign = "dependencies",
                      live = true,
                      mode = 2,
                      type = { "string" },
                      lone = true,
                      scan = "letter.id" },
                    { sign = "scripts",
                      live = true,
                      mode = 3,
                      type = { "string" },
                      scan = "page" },
                    { sign = "styles",
                      live = true,
                      mode = 3,
                      type = { "string" },
                      scan = "page" },
                    { sign = "datas",
                      live = true,
                      mode = 3,
                      type = { "string" },
                      scan = "page" },
                    { sign = "peers",
                      live = true,
                      mode = 3,
                      type = { "string" },
                      lone = true },
                    { sign = "messages",
                      live = false,
                      mode = 3,
                      type = { "string" },
                      lone = true,
                      scan = "rootpage" },
                    { sign = "Comment",
                      live = false,
                      mode = 4,
                      type = "string" }
                  }
VALUES.arrays =   { actions       = false,
                    contentModels = false,
                    skins         = false,
                    targets       = { { "desktop", true },
                                      { "mobile",  true } }
                  }
VALUES.radio =    { type = { { "general", true },
                             { "styles",  true } }
                  }
VALUES.validate = { LetterID       = { { "%u%a+" } },
                    lang           = { { "%l%l%l?",
                                         "%l%l%l?-%a+" } },
                    ["letter-_ID"] = { { "%l[%-_%a]+%a" },
                                       { "[%-_][%-_]" } },
                    ["letter-id"]  = { { "%l[%-%l]+%l" },
                                       { "--" } },
                    ["letter.id"]  = { { "%l[%.%l]+%l" },
                                       { "%.%." } },
                    namespace      = { { "-?[1-9]%d*" } },
                    page           = { { "[^#%[%]<>{}|]+" },
                                       { "[ _][ _]" } },
                    rootpage       = { { "[^/#%[%]<>{}|]+" },
                                       { "[ _][ _]" } }
                  }
I18N.texts =
    { ActiveSpec           = { en = "Active specification.",
                               de = "Aktive Spezifikation." },
      Assigned             = { en = "Assigned",
                               de = "Zuweisung" },
      AtLine               = { en = "at line",
                               de = "in Zeile" },
      BadAssigned          = { en = "assignment to sole boolean",
                               de = "Wertzuweisung an boolean" },
      BadLines             = { en = "Bad definition lines" },
      BadNamespace         = { en = "Bad namespace",
                               de = "Namensraum ungültig" },
      BadTemplateArg       = { en = "Template argument no string",
                               de = "Vorlagenparameter kein string" },
      BadTemplateArgs      = { en = "Template arguments no table",
                               de = "Vorlagenparameter keine table" },
      defaultTrue          = { en = "default for all",
                               de = "Vorgabe für alle" },
      defaultFalse         = { en = "activation needed",
                               de = "benötigt Aktivierung" },
      Detail               = { en = "Property",
                               de = "Eigenschaft" },
      hiddenTrue           = { en = "hidden",
                               de = "versteckt" },
      hiddenFalse          = { en = "configurable",
                               de = "konfigurierbar" },
      IDmissing            = { en = "ID missing",
                               de = "ID erforderlich" },
      InvalidHeadID        = { en = "Invalid headline ID",
                               de = "Ungültige Überschrift-ID" },
      InvalidHeadline      = { en = "Invalid headline",
                               de = "Ungültige Überschrift" },
      InvalidID            = { en = "Invalid gadget ID",
                               de = "Ungültige Gadget ID" },
      InvalidResource      = { en = "Invalid resource page name",
                               de = "Seitenname der Ressource ungültig" },
      InvalidScope         = { en = "Scope invalid",
                               de = "Ungültiger Scope" },
      MissingValue         = { en = "Missing value",
                               de = "Wert fehlt" },
      NoClosingBracket     = { en = "Closing bracket missing",
                               de = "Schließende Klammer fehlt" },
      NoCommentsYet        = { en = "Comments not yet available",
                               de = "Kommentare noch nicht möglich" },
      NoGadgetID           = { en = "Gadget ID missing",
                               de = "Gadget ID erforderlich" },
      NoID                 = { en = "Missing identifier",
                               de = "Bezeichner fehlt" },
      NoOptionID           = { en = "Option name missing",
                               de = "Optionsname fehlt" },
      NoResources          = { en = "No resource page name",
                               de = "Kein Seitenname einer Ressource" },
      packageTrue          = { en = "package support",
                               de = "package-Unterstützung" },
      packageFalse         = { en = "no package support",
                               de = "keine package-Unterstützung" },
      RepeatedID           = { en = "Gadget ID repeated",
                               de = "Gadget ID wiederholt" },
      RepeatedOption       = { en = "Option name repeated",
                               de = "Optionsname wiederholt" },
      ResourceLoaderTrue   = { en = "supported",
                               de = "unterstützt" },
      ResourceLoaderFalse  = { en = "not supported",
                               de = "nicht unterstützt" },
      SpecMismatch         = { en = "Specification mismatch:",
                               de = "Spezifikation abweichend:" },
      supportsUrlLoadTrue  = { en = "URL loading supported",
                               de = "Laden per URL unterstützt" },
      supportsUrlLoadFalse = { en = "URL loading not supported",
                               de = "Laden per URL nicht unterstützt" },
      topTrue              = { en = "top loading",
                               de = "top-Laden" },
      topFalse             = { en = "regular loading sequence",
                               de = "normale Ladereihenfolge" },
      UnknownOptionID      = { en = "Unknown option name",
                               de = "Optionsname unbekannt" },
      Whitespace           = { en = "Superfluous whitespace",
                               de = "Überflüssiger Whitespace" }
    }



local find = function ( area, at )
    -- Check for existing page
    -- Precondition:
    --     area    -- number, of namespace, or not
    --     at      -- string, with name of page, or mw.title
    -- Postcondition:
    --     Returns true if page existing
    local r, t
    if area then
        t = mw.title.makeTitle( area, at )
    elseif type( at ) == "table" then
        t = at
    end
    if t and  t.protectionLevels.edit then
        r = true
    end
    return r
end -- find()



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



ACTIVE.first = function ()
    -- Initialize site effective definitions
    local source = "Gadgets-definition"
    local ns, s, t
    ACTIVE.start = "Gadget-"
    if GLOBAL.scope == "site" then
        s = GLOBAL.config["2302"]
        if type( s ) == "string"  and
           s ~= ""  and
           s ~= "-" then
            ns           = 2302
            source       = s
            ACTIVE.ns    = 2300
            ACTIVE.start = ""
        else
            ns = 8
        end
    elseif GLOBAL.scope == "global" then
    elseif GLOBAL.scope == "text" then
    else
        ns, s = GLOBAL.focus( GLOBAL.scope )
        if ns then
            ACTIVE.ns = ns
            if s:sub( -1, 1 ) ~= "/" then
                s = s .. "/"
            end
            source       = s .. source
            ACTIVE.start = s .. "Gadget-"
        end
    end
    if ns then
        t             = mw.title.makeTitle( ns, source )
        ACTIVE.ns     = ACTIVE.ns or ns
        ACTIVE.source = t.prefixedText
        ACTIVE.live   = find( false, t )
        if ACTIVE.live then
            ACTIVE.story = GLOBAL.frame():expandTemplate{ title = t }
            ACTIVE.title = t
        end
    end
end  -- ACTIVE.first()



COMMONS.factory = function ( all, access )
    -- Retrieve global configuration
    -- Precondition:
    --     all       -- table, with raw tabular data
    --     access    -- string, with keyword
    -- Postcondition:
    --     Returns table, with configuration data
    local r = { }
    local t = { }
    local e, s, v
    table.insert( t, "string" )
    if access == "modules" then
        table.insert( t, "string" )
    elseif access == "actions" then
        table.insert( t, "boolean" )
    elseif access == "contentModels" then
    elseif access == "skins" then
        table.insert( t, "boolean" )
    end
    for i = 1, #all do
        e = all[ i ]
        if type( e ) == "table"  and
           type( e[ 1 ] ) == "string" then
            s = mw.text.trim( e[ 1 ] )
            if s ~= ""  and
               not  s:find( "[ \"\n=&|{}%[%]<>#]" ) then
                v = e[ 2 ]
                if t[ 2 ] then
                    if type( v ) ~= t[ 2 ] then
                        s = false
                    end
                elseif access == "contentModels" then
                    v = true
                end
                if s then
                    if access == "modules" then
                        r[ s ] = v
                    else
                        e = { }
                        table.insert( e, s )
                        table.insert( e, v )
                        table.insert( r, e )
                    end
                end
            end
        end
    end -- for i
    return r
end  -- COMMONS.factory()



COMMONS.fetch = function ( access )
    -- Retrieve global configuration
    -- Precondition:
    --     access    -- string, with keyword
    -- Postcondition:
    --     Returns table, with configuration data, or true if unavailable
    COMMONS.tabDATA = COMMONS.tabDATA  or  { }
    if not COMMONS.tabDATA[ access ] then
        local storage = COMMONS.tabs[ access ]
        local v
        if storage then
            local lucky, data
            storage = string.format( "%s.tab", storage )
            lucky, data = pcall( mw.ext.data.get, storage )
            if type( data ) == "table" then
                data = data.data
                if type( data ) == "table" then
                    v = data
                end
            end
        end
        if v then
            v = COMMONS.factory( v, access )
        else
            v = true
        end
        COMMONS.tabDATA[ access ] = v
    end
    return COMMONS.tabDATA[ access ]
end  -- COMMONS.fetch()



ERROR.fault = function ( about, area )
    -- Register an error
    -- Precondition:
    --     about    -- string, with message
    --     area     -- string, with field, or not
    local e = { }
    ERROR.errors = ERROR.errors  or  { }
    table.insert( e, about )
    table.insert( e, area )
    table.insert( ERROR.errors, e )
end  -- ERROR.fault()



ERROR.flat = function ( about, align )
    -- Format code
    -- Precondition:
    --     about    -- string, with message
    --     align    -- true, if string requested
    -- Postcondition:
    --     Returns mw.html, or string
    local r = mw.html.create( "code" )
                     :wikitext( mw.text.nowiki( about ) )
    if align then
        r = tostring( r )
    end
    return r
end  -- ERROR.flat()




ERROR.full = function ()
    -- Retrieve list of all error messages
    -- Postcondition:
    --     Returns mw.html, or not
    local r
    return r
end  -- ERROR.full()



GLOBAL.features = function ()
    -- Retrieve local configuration
    -- Postcondition:
    --     Configuration updated where requested
    local site = GLOBAL.frame():getTitle() .. "/local"
    local seek = string.format( "^%s:(.+)$",
                                mw.site.namespaces[ 828 ].name )
    site = site:match( seek, 1 )
    if find( 828, site ) then
        local lucky, config = pcall( mw.loadData, site )
        if type( config ) == "table" then
            if type( config.modules ) == "table" then
                for k, v in pairs( config.modules ) do
                    GLOBAL.modules[ k ] = v
                end -- for k, v
            end
            if type( config.actions ) == "table" then
                for k, v in pairs( config.actions ) do
                    VALUES.arrays.actions[ k ] = v
                end -- for k, v
            end
            if type( config.IDexceptions ) == "table" then
                for k, v in pairs( config.IDexceptions ) do
                    GLOBAL.IDexceptions[ k ] = v
                end -- for k, v
            end
        end
    end
end  -- GLOBAL.features()



GLOBAL.first = function ()
    -- Initialize global and local configuration, if not yet done
    -- Postcondition:
    --     Configuration up to date
    if not GLOBAL.config then
        local config = COMMONS.fetch( "config" )
        if type( config ) ~= "table" then
            config = { }
        end
        GLOBAL.config = config
        VALUES.full()
        config = COMMONS.fetch( "modules" )
        if type( config ) ~= "table" then
            config = { }
        end
        GLOBAL.modules = config
        GLOBAL.IDexceptions = { }
        GLOBAL.features()
        OPTIONS.first()
    end
end  -- GLOBAL.first()



GLOBAL.focus = function ( area )
    -- Analyze page name in name space
    -- Precondition:
    --     area    -- string, with page name, not in gadget spaces
    -- Postcondition:
    --     Returns:
    --         1. namespace number
    --         2. normalized page title
    local i = area:find( ":", 1, true )
    local rNS, rT
    if i then
        local s
        if i == 1 then
            rNS = 0
            rT  = area:sub( i + 1 )
        else
            local q
            s = mw.text.trim( area:sub( 1,  i - 1 ) )
            q = mw.site.namespaces[ s ]
            if q  and
               q.id ~= 8  and
               q.id ~= 2300  and
               q.id ~= 2302 then
                rNS = q.id
            end
        end
        if rNS then
            s  = mw.text.trim( area:sub( i + 1 ) )
            rT = string.format( "%s:%s",
                                mw.site.namespaces[ rNS ].name,
                                s )
        end
    else
        rNS = 0
        rT  = area
    end
    return  rNS, rT
end  -- GLOBAL.focus()



GLOBAL.frame = function ( frame )
    -- Retrieve and memorize frame object
    -- Precondition:
    --     frame    -- frame object, or not
    -- Postcondition:
    --     Returns frame object
    if frame then
        GLOBAL.Frame = frame
    end
    if not GLOBAL.Frame then
        GLOBAL.Frame = mw.getCurrentFrame()
    end
    return GLOBAL.Frame
end  -- GLOBAL.frame()



HTML.feature = function ( at, assigned, about, fault )
    -- Format single gadget option table row
    -- Precondition:
    --     at          -- string, with gadget option name
    --     assigned    -- gadget option value
    --     about       -- table, with gadget option configuration
    --     fault       -- function, as error handler
    -- Postcondition:
    --     Returns mw.html object <tr>
    local r   = mw.html.create( "tr" )
    local td1 = mw.html.create( "td" )
    local td2 = mw.html.create( "td" )
    local e, ns, s
    td1:wikitext( at )
    if at:sub( 1, 1 ) == "-" then
        s = at:sub( 2 )
        td1:attr( "data-sort-value",  s .. "-" )
    else
        s = at
    end
    if about.type == "boolean" then
        if assigned then
            s = s .. "True"
        else
            s = s .. "False"
        end
        td2:wikitext( I18N.fetch( s ) )
    elseif type( about.type ) == "table"  and
           type( assigned ) == "table" then
        if s == "targets"  or
           s == "skins"  or
           s == "rights"  or
           s == "actions"  or
           s == "contentModels"  or
           s == "pageprops"  or
           s == "groups"  or
           s == "type" then
            for i = 1, #assigned do
                e = mw.html.create( "code" )
                           :css( "margin-right", "0.4em" )
                           :css( "white-space", "nowrap" )
                           :wikitext( assigned[ i ] )
                td2:node( e )
            end -- for i
        elseif s == "namespaces" then
            local q
            for i = 1, #assigned do
                s  = assigned[ i ]
                ns = tonumber( s )
                e  = mw.html.create( "code" )
                            :css( "margin-right", "0.4em" )
                            :wikitext( s )
                q  = mw.site.namespaces[ ns ]
                if q then
                    e:attr( "title",   q.name .. ":" )
                else
                    e:css( "background-color", "#FF0000" )
                    e:css( "color", "#FFFF00" )
                    e:css( "font-weight", "bold" )
                end
                td2:node( e )
            end -- for i
        elseif s == "specials" then
            local space = mw.site.namespaces.special.name
            local k     = #space + 2
            for i = 1, #assigned do
                s = mw.text.nowiki( assigned[ i ] )
                e = mw.html.create( "code" )
                           :css( "margin-right", "0.4em" )
                           :wikitext( s )
                s = GLOBAL.frame():callParserFunction( "#special",
                                                       assigned[ i ] )
                e:attr( "title",  s:sub( k ) )
                td2:node( e )
            end -- for i
        elseif s == "transcludes"  or
               s == "categories" then
            local link14 = ( s == "categories" )
            for i = 1, #assigned do
                s = mw.text.nowiki( assigned[ i ] )
                if link14 then
                    s = string.format( ":%s:%s",
                                       mw.site.namespaces[14].name,
                                       s )
                end
                s = string.format( "[[%s]]", s )
                e = mw.html.create( "span" )
                           :css( "margin-right", "0.4em" )
                           :css( "white-space", "nowrap" )
                e:wikitext( s )
                td2:node( e )
            end -- for i
        elseif s == "userlangs"  or
               s == "contentlangs" then
        elseif s == "dependencies" then
            local hash = COMMONS.tabDATA.modules
            if type( hash ) ~= "table" then
                hash = false
            end
            for i = 1, #assigned do
                s = mw.text.nowiki( assigned[ i ] )
                e = mw.html.create( "span" )
                           :css( "margin-right", "0.4em" )
                           :css( "white-space", "nowrap" )
                if hash and hash[ s ] then
                    s = string.format( "[[%s|%s]]",  hash[ s ],  s )
                end
                e:wikitext( s )
                td2:node( e )
            end -- for i
        elseif s == "scripts"  or
               s == "styles"  or
               s == "datas"  or
               s == "peers" then
            local t
            for i = 1, #assigned do
                s = mw.text.nowiki( assigned[ i ] )
                t = mw.title.makeTitle( ACTIVE.ns,
                                        ACTIVE.start .. s )
                e = mw.html.create( "span" )
                           :css( "margin-right", "0.4em" )
                           :css( "white-space", "nowrap" )
                           :wikitext( string.format( "[[%s|%s]]",
                                                     t.prefixedText,
                                                     s ) )
                td2:node( e )
            end -- for i
        elseif s == "messages" then
        else
            td2:wikitext( tostring(assigned) )
        end
    else
        td2:wikitext( tostring(assigned) )
    end
    if not about.live then
        r:css( "background-color",  "#" .. HTML.bgcolorSleeping )
    end
    r:node( td1 )
     :node( td2 )
    return r
end  -- HTML.feature()



HTML.features = function ( assigned )
    -- Format gadget options table
    -- Precondition:
    --     assigned    -- table, with gadget options
    -- Postcondition:
    --     Returns mw.html object <table>
    local r = mw.html.create( "table" )
    local n = 0
    local fault = function ( add )
                      assigned.ERRORS = assigned.ERRORS  or  { }
                      table.insert( assigned.ERRORS, add )
                  end
    local e, o, s, td, th1, th2
    r:addClass( "wikitable" )
    if assigned.ID then
        local t
        s = mw.text.nowiki( assigned.ID )
        t = mw.title.makeTitle( 8,  "Gadget-" .. s )
        e = mw.html.create( "caption" )
                   :addClass( "mw-code" )
                   :attr( "id", s )
                   :wikitext( string.format( "[[%s|%s]]",
                                             t.prefixedText, s ) )
        r:newline()
         :node( e )
        if find( false, t ) then
            s  = GLOBAL.frame():callParserFunction( "int",
                                                    t.text )
            td = mw.html.create( "td" )
                        :attr( "colspan", "2" )
                        :wikitext( s )
            r:newline()
             :node( mw.html.create( "tr" )
                           :addClass( "sortbottom" )
                           :node( td ) )
        end
    else
        fault( I18N.fetch( "NoID" ) )
    end
    th1 = mw.html.create( "th" )
                 :wikitext( I18N.fetch( "Detail" ) )
    th2 = mw.html.create( "th" )
                 :addClass( "unsortable" )
                 :wikitext( I18N.fetch( "Assigned" ) )
    r:newline()
     :node( mw.html.create( "tr" )
                   :node( th1 )
                   :node( th2 ) )
    for i = 2,  #OPTIONS.order - 1 do
        o = OPTIONS.order[ i ]
        s = o.sign
        e = assigned[ s ]
        if type( e ) ~= "nil" then
            r:newline()
             :node( HTML.feature( s, e, o, fault ) )
            n = n + 1
        end
    end -- for i
    if n > 1 then
        r:addClass( "sortable" )
    end
    if assigned.Comment then
        td = mw.html.create( "td" )
                    :addClass( "sortbottom" )
                    :attr( "colspan", "2" )
                    :wikitext( assigned.Comment )
        r:newline()
         :node( mw.html.create( "tr" )
                       :node( td ) )
    end
    if assigned.ERRORS then
        e = mw.html.create( "ul" )
        for i = 1, #assigned.ERRORS do
            e:node( mw.html.create( "li" )
                           :wikitext( assigned.ERRORS[ i ] ) )
        end -- for i
        td = mw.html.create( "td" )
                    :addClass( "error" )
                    :addClass( "sortbottom" )
                    :attr( "colspan", "2" )
                    :node( e )
        r:newline()
         :node( mw.html.create( "tr" )
                       :node( td ) )
    end
    r:newline()
    return r
end  -- HTML.features()



HTML.flat = function ( apply )
    -- Format coded value
    -- Precondition:
    --     apply    -- string, with value
    -- Postcondition:
    --     Returns mw.html object <div>
    local e = mw.html.create( "pre" )
    local r = mw.html.create( "div" )
    e:addClass( "mw-code" )
     :css( "overflow", "auto" )
     :css( "margin-right", "0.5em" )
     :css( "margin-top", "0.5em" )
     :css( "white-space", "nowrap" )
     :wikitext( mw.text.nowiki( apply ) )
    r:addClass( "mw-highlight mw-highlight-lang-text" )
     :addClass( "mw-highlight-copy" )
     :css( "margin-right", "2em" )
     :css( "padding-top", "1em" )
     :newline()
     :node( e )
    return r
end  -- HTML.flat()



HTML.folder = function ( assigned )
    -- Build gadget options table with resulting definition row and link
    -- Precondition:
    --     assigned    -- table, with gadget options
    -- Postcondition:
    --     Returns mw.html object <div>
    local e = mw.html.create( "div" )
                     :wikitext( string.format( "[[%s]]",
                                               ACTIVE.source ) )
    local g = ROW.fiat( assigned )
    local r = mw.html.create( "div" )
                     :node( HTML.features( assigned ) )
                     :newline()
                     :node( HTML.flat( g ) )
                     :newline()
                     :node( e )
    local d = ROW.found( assigned.ID )
    if d then
        d = OPTIONS.fair( assigned, d )
        e = mw.html.create( "div" )
        if d then
            local diff = mw.html.create( "div" )
            local p    = mw.html.create( "span" )
            p:css( "color", "#FF0000" )
             :css( "font-weight", "bold" )
             :wikitext( I18N.fetch( "SpecMismatch" ) )
            e:node( p )
            diff:css( "margin-left", "1em" )
                :css( "margin-right", "1em" )
            for k, v  in pairs( d ) do
                diff:node( mw.html.create( "code" )
                                  :wikitext( k, " " ) )
                for i = 1, #v do
                    p = v[ i ]
                    diff:wikitext( " " )
                    diff:node( mw.html.create( "em" )
                                  :wikitext( mw.text.nowiki( p[ 1 ] ) ) )
                    diff:wikitext( " " )
                    diff:node( mw.html.create( "strong" )
                                  :wikitext( mw.text.nowiki( p[ 2 ] ) ) )
                end -- for i
                diff:node( mw.html.create( "br" ) )
            end -- for k, v
            e:node( diff )
        else
            e:css( "color", "#00A000" )
             :css( "font-weight", "bold" )
             :wikitext( I18N.fetch( "ActiveSpec" ) )
        end
        r:newline()
         :node( e )
    end
    return r
end  -- HTML.folder()



I18N.fetch = function ( ask )
    -- Retrieve localized text
    -- Precondition:
    --     ask    -- string, with keyword
    -- Postcondition:
    --     Returns string, with translation
    local e = I18N.texts[ ask ]
    local r
    if e then
        if not I18N.slang then
            I18N.slang = mw.language.getContentLanguage():getCode()
                                                         :lower()
        end
        r = e[ I18N.slang ]
        if not e then
            r = e.en
        end
    end
    return r  or  string.format( "I18N{%s}", ask )
end  -- I18N.fetch()



OPTIONS.fair = function ( a1, a2 )
    -- Compare two definitions
    -- Precondition:
    --     a1    -- table, with definitions
    --     a2    -- table, with definitions
    -- Postcondition:
    --     Returns hash table, of mismatching assignments
    local l, o, r, s, v1, v2
    for i = 1, #OPTIONS.order do
        o = OPTIONS.order[ i ]
        if o.live then
            s  = o.sign
            v1 = a1[ s ]
            v2 = a2[ s ]
            if v1  and  v2  and  v1 ~= v2 then
                if type( v1 ) == "table"  and
                   type( v2 ) == "table" then
                    if #v1 == #v2 then
                        l = false
                        for k = 1, #v1 do
                            if v1[ k ] ~= v2[ k ] then
                                l = true
                                break -- for k
                            end
                        end -- for k
                        if not l then
                            v1 = false
                            v2 = false
                        end
                    end
                end
                if v1 ~= v2 then
                    r = r  or  { }
                    o = { }
                    table.insert( o, v1 )
                    table.insert( o, v2 )
                    r[ s ] = o
                end
            end
        end
    end -- for i
    return r
end  -- OPTIONS.fair()



OPTIONS.first = function ()
    -- Initialize gadget options configuration
    -- Postcondition:
    --     Configuration up to date
    local e, s, u
    OPTIONS.hash  = { }
    OPTIONS.order = { }
    for i = #OPTIONS.config, 1, -1 do
        e = OPTIONS.config[ i ]
        if not e.live then
            s = GLOBAL[ e.sign ]
            if s == "1"  or
               s == true then
                e.live = true
            end
        end
        s = e.sign
        OPTIONS.hash[ s ] = e
        if e.mode then
            if e.mode == 1  and
               not e.lone  and
               e.type ~= "boolean" then
                s = "-" .. e.sign
                if not OPTIONS.hash[ s ] then
                    u = { sign = s,
                          live = false,
                          mode = 1,
                          type = e.type,
                          scan = e.scan }
                    table.insert( OPTIONS.order, 1, u )
                    OPTIONS.hash[ s ] = u
                    table.insert( OPTIONS.config, u )
                end
            end
            table.insert( OPTIONS.order, 1, e )
        end
    end -- for i
end  -- OPTIONS.first()



ROW.failed = function ( assigned, at )
    -- Create line number referenced error in Gadgets-definition
    -- Precondition:
    --     assigned    -- string, with bad data
    --     at          -- number, with line number
    -- Postcondition:
    --     Returns string, with formatted message, leading space
    local r
    if type( assigned ) == "string"  and
       type( at ) == "number" then
        r = mw.html.create( "span" )
                   :node( ERROR.flat( assigned ) )
                   :wikitext( string.format( " %s ",
                                             I18N.fetch( "AtLine" ) ) )
                   :node( mw.html.create( "code" )
                                 :wikitext( string.format( "%d", at ) ) )
        r = " " .. tostring( r )
    end
    return r or ""
end  -- ROW.failed()



ROW.fetch = function ( all, access )
    -- Parse line in Gadgets-definition
    -- Precondition:
    --     all       -- table, with all line specifications
    --                  .hash  -- table, with gadget ID mapping
    --                  .rows  -- sequence table, with line content
    --     access    -- number or string, to be accessed
    -- Postcondition:
    --     Returns table, with line properties
    local r
    if type( all ) == "table" then
        local errors, slot
        local fault = function ( add )
                              errors = errors  or  { }
                              local s = add
                              if slot then
                                  s = s .. ROW.failed( slot,
                                                       all.hash[ slot ] )
                              end
                              table.insert( errors, s )
                      end
        local s = type( access )
        local n
        if s == "number" then
            n = access
        elseif s == "string" then
            n    = all.hash[ access ]
            slot = access
        end
        if n then
            local story = all.rows[ n ]
            if type( story ) == "string"  and
               story ~= "" then
                local i, j, o, opts, ress
                story = story:gsub( "^%s*%*%s*", "" )
                r = { }
                if type( ROW.comment ) == "table"  and
                   type( ROW.comment.suffix ) == "string"  and
                   ROW.comment.suffix ~= "" then
                    i = story:find( ROW.comment.suffix )
                    if i then
                        if i == 1 then
                            r.Comment = story:sub( 2 )
                        else
                            r.Comment = story:sub( i + 1 )
                            story     = story:sub( 1,  i - 1 )
                            story     = mw.text.trim( story )
                        end
                        r.Comment = mw.text.trim( r.Comment )
                    end
                end
                if r.Comment  and
                   not  ( ROW.comment.live or ROW.lazy ) then
                    fault( I18N.fetch( "NoCommentsYet" ) )
                end
                if not ROW.lazy then
                    fault( I18N.fetch( "Whitespace" ) )
                end
                i = story:find( "[", 1, true )
                if i then
                    if i > 1 then
                        slot = mw.text.trim( story:sub( 1,  i - 1 ) )
                        j = story:find( "]",  i + 1,  true )
                        if j then
                            s    = mw.text.trim( story:sub( i + 1,
                                                            j - 1 ) )
                            opts = mw.text.split( s, "%s*|%s*" )
                            if #opts < 1 then
                                opts = false
                            end
                            story = mw.text.trim( story:sub( j + 1 ) )
                        else
                            fault( I18N.fetch( "NoClosingBracket" ) )
                            story = ""
                        end
                    else
                        slot  = ""
                        story = ""
                    end
                else
                    i = story:find( "|", 1, true )
                    if i then
                        slot  = mw.text.trim( story:sub( 1,  i - 1 ) )
                        story = mw.text.trim( story:sub( i + 1 ) )
                    else
                        slot  = ""
                        story = ""
                    end
                end
                if slot == "" then
                    fault( I18N.fetch( "NoGadgetID" ) )
                    ress = { }
                else
                    ress = VALUES.feed( story, "%s*|%s*" )  or  { }
                end
                if opts then
                    local set, v
                    for i = 1, #opts do
                        s = mw.text.trim( opts[ i ] )
                        j = s:find( "=", i, true )
                        if j then
                            if j == 1 then
                                set = false
                                o   = false
                                s   = string.format( "%s %s",
                                             ERROR.flat( slot, true ),
                                             I18N.fetch( "NoOptionID" ) )
                                fault( s )
                            else
                                set = s:sub( j + 1 )
                                set = mw.text.trim( set )
                                s   = s:sub( 1,  j - 1 )
                                s   = mw.text.trim( s )
                                o   = OPTIONS.hash[ s ]
                            end
                        else
                            o   = OPTIONS.hash[ s ]
                            set = false
                        end
                        if o then
                            if r[ o.sign ] then
                                s = string.format( "%s&#124;%s",
                                                   slot,
                                                   o.sign )
                                s = string.format( "%s %s",
                                         ERROR.flat( s, true ),
                                         I18N.fetch( "RepeatedOption" ) )
                                fault( s )
                            elseif type( o.type ) == "table" then
                                if set then
                                    v = VALUES.feed( set,
                                                     ",",
                                                     o.type[ 1 ]
                                                            == "number" )
                                else
                                    v = false
                                end
                                if type( v ) == "table" then
                                    if o.sign == "peers" then
                                        ROW.fill( r, v, "peers", fault )
                                    end
                                else
                                    s = string.format( "%s&#124;%s=",
                                                       slot,
                                                       o.sign )
                                    s = string.format( "%s %s",
                                                       s,
                                           I18N.fetch( "MissingValue" ) )
                                    fault( s )
                                end
                            elseif o.type == "boolean" then
                                if set then
                                    v = false
                                    s = string.format( "%s&#124;%s=",
                                                       slot,
                                                       o.sign )
                                    s = string.format( "%s %s",
                                                       s,
                                           I18N.fetch( "BadAssigned" ) )
                                else
                                    v = true
                                end
                            elseif o.type == "string" then
                                if set then
                                    s = string.format( "%s&#124;%s=",
                                                       slot,
                                                       o.sign )
                                    s = string.format( "%s %s",
                                                       s,
                                           I18N.fetch( "MissingValue" ) )
                                    fault( s )
                                else
                                    v = set
                                end
                            end
                            if v then
                                r[ o.sign ] = v
                            end
                        else
                            s = string.format( "%s %s",
                                         I18N.fetch( "UnknownOptionID" ),
                                         ERROR.flat( s, true ) )
                            fault( s )
                        end
                    end -- for i
                end
                if #ress > 0 then
                    ROW.fill( r, ress, false, fault )
                elseif o  and  o.sign ~= "peers" then
                    fault( I18N.fetch( "NoResources" ) )
                end
            end
        end
    end
    return r
end  -- ROW.fetch()



ROW.fiat = function ( about )
    -- Create line for Gadgets-definition
    -- Precondition:
    --     about    -- table, with gadget options
    -- Postcondition:
    --     Returns string, with line
    local r
    if about.ID then
        local o, set, sources, v
        r = string.format( "* %s", about.ID )
        for i = 2, #OPTIONS.order do
            o = OPTIONS.order[ i ]
            if o.live then
                v = about[ o.sign ]
                if v then
                    if o.mode == 1  or  o.mode == 2 then
                        if set then
                            set = set .. "|"
                        else
                            set = ""
                        end
                        set = set .. o.sign
                        if type( o.type ) == "table"  and
                           type( v ) == "table" then
                            set = set .. "="
                            for k = 1, #v do
                                if k > 1 then
                                    set = set .. ","
                                end
                                set = set .. v[ k ]
                            end -- for k
                        end
                    elseif o.mode == 3 then
                        sources = sources  or  ""
                        for k = 1, #v do
                            sources = string.format( "%s|%s",
                                                     sources,
                                                     v[ k ] )
                        end -- for k
                    end
                end
            end
        end -- for i
        if set then
            r = string.format( "%s[%s]", r, set )
        end
        if sources then
            r = r .. sources
        end
    else
        r = "*"
    end
    return r
end  -- ROW.fiat()



ROW.fill = function ( all, assign, ancestor, fault )
    -- Distribute resource pages
    -- Precondition:
    --     all         -- table, with gadget options
    --     about       -- sequence table, with resource pages
    --     ancestor    -- "peers", or not
    --     fault       -- function, as error handler
    -- Postcondition:
    --     resource page has been added to all table
    if type( all ) == "table"  and
       type( assign ) == "table" then
        local types
        local src, swap, types
        if ancestor == "peers" then
            types = { peers = "%.css$" }
        else
            types = { scripts = "%.js$",
                      styles  = "%.css$",
                      datas   = "%.json$" }
        end
        for i = 1, #assign do
            src  = assign[ i ]
            swap = false
            for k, v in pairs( types ) do
                if src:find( v ) then
                    swap = k
                end
            end -- for k, v
            if swap then
                all[ swap ] = all[ swap ]  or  { }
                table.insert( all[ swap ], src )
            else
                fault( string.format( "%s %s",
                                      I18N.fetch( "InvalidResource" ),
                                      ERROR.flat( src, true ) ) )
            end
        end -- for i
    end
end  -- ROW.fill()



ROW.first = function ( all, apply )
    -- Convert Gadgets-definition lines into tables
    -- Precondition:
    --     all      -- string, with all Gadgets-definition lines
    --     apply    -- mw.title or string, with origin
    -- Postcondition:
    --     Returns table, with
    --             .rows      -- sequence table of line strings
    --             .hash      -- mapping table of gadget ID to lines
    --             .elts      -- sequence table of heading IDs and lines
    --             .origin    -- mw.title or string
    local r
    if type( all ) == "string" then
        local errors
        local fault = function ( add )
                          errors = errors  or  { }
                          table.insert( errors, add )
                      end
        local elts = { }
        local hash = { }
        local rows = mw.text.split( all, "%s*\n%s*" )
        local k, s, scream
        r = { origin = apply }
        for i = 1, #rows do
            s = mw.text.trim( rows[ i ] )
            if s ~= "" then
                if s:sub( 1, 2 ) == "==" then
                    scream = false
                    k, s = s:match( "^(==+)%s*([^%s=].+[^%s=])%s*%1$" )
                    if s then
                        table.insert( elts, s )
                        if not VALUES.fine( s, "rootpage" ) then
                            scream = "InvalidHeadID"
                        end
                    else
                        scream = "InvalidHeadline"
                    end
                    if scream then
                        s = I18N.fetch( scream ) .. ROW.failed( "==", i )
                        fault( s )
                    end
                else
                    s = s:gsub( "^%s*%*%s*", "" )
                    k = s:find( "[%[|]" )
                    if k then
                        if k == 1 then
                            s = ""
                        else
                            s = mw.text.trim( s:sub( 1,  k - 1 ) )
                        end
                    end
                    if s ~= "" then
                        scream = false
                        table.insert( elts, i )
                        if hash[ s ] then
                            scream = "RepeatedID"
                        else
                            hash[ s ] = i
                            if not VALUES.fine( s, "rootpage" ) then
                                scream = "InvalidID"
                            end
                        end
                        if scream then
                            s = I18N.fetch( scream )
                                .. ROW.failed( s, i )
                            fault( s )
                        end
                    end
                end
            end
        end -- for i
        r.rows  = rows
        r.elems = elts
        r.hash  = hash
        if errors then
            s = I18N.fetch( "BadLines" ) .. ROW.from( apply )
            table.insert( errors, s, 1 )
            for i = 1, #errors do
                ERROR.fault( errors[ i ], "ROW" )
            end -- for i
        end
    end
    return r
end  -- ROW.first()



ROW.found = function ( ask )
    -- Retrieve single entry from active
    -- Precondition:
    --     ask    -- string, with entry ID
    local r
    if ACTIVE.live  and  not ACTIVE.def then
        ACTIVE.def = ROW.first( ACTIVE.story, ACTIVE.title )
    end
    if ACTIVE.def then
        r = ROW.fetch( ACTIVE.def, ask )
    end
    return r
end  -- ROW.found()



ROW.from = function ( all )
    -- Format origin
    -- Precondition:
    --     all    -- table, with Gadgets-definition representation
    -- Postcondition:
    --     Returns string, with hint
    local r
    if type( all ) == "table" then
        if type( all.origin ) == "table" then
            local s = all.origin.prefixedText
            if type( s ) == "string" then
                r = string.format( "[[%s]]", s )
            else
                r = "??????????"
            end
        else
            r = "TEXT"
        end
    end
    return r or ""
end  -- ROW.from()



TEMPLATE.fill = function ( all )
    -- Convert template parameters into hash
    -- Precondition:
    --     all    -- table, with template parameters
    -- Postcondition:
    --     Returns table, with options
    local r = { }
    if type( all ) == "table" then
        local errors
        local fault = function ( add )
                          r.ERRORS = r.ERRORS  or  { }
                          table.insert( r.ERRORS, add )
                      end
        local n, o, s, seps, vals
        r = { }
        for k, v in pairs( all ) do
            if type( v ) == "string" then
                o = OPTIONS.hash[ k ]
                if o then
                    s = type( o.type )
                    if s == "string" then
                        s = o.type
                    elseif s == "table" then
                        s = string.format( "{%s}", o.type[ 1 ] )
                    else
                        s = false
                    end
                    if s == "boolean" then
                        if v == "1" then
                            r[ k ] = true
                        else
                            r[ k ] = false
                        end
                    elseif s == "string" then
                        r[ k ] = v
                    elseif s then
                        v = v:gsub( "&#124;", "|" )
                             :gsub( "&#x7C;", "|" )
                             :gsub( "&#x7c;", "|" )
                        if o.scan == "page" then
                            seps = "%s*|%s*"
                        else
                            seps = "[%s,|]+"
                        end
                        vals = mw.text.split( v, seps )
                        for i = 1, #vals do
                            v = mw.text.trim( vals[ i ] )
                            if v == "" then
                                v = false
                            elseif s == "{number}" then
                                n = tonumber( v )
                                if n then
                                    v = n
                                else
                                    s = string.format( "%s %s",
                                            I18N.fetch( "BadNamespace" ),
                                            ERROR.flat( v, true ) )
                                    v = false
                                end
                            end
                            if v then
                                r[ k ] = r[ k ]  or  { }
                                table.insert( r[ k ],  v )
                            end
                        end -- for i
                    end
                else
                    s = string.format( "%s %s",
                                       I18N.fetch( "UnknownOptionID" ),
                                       ERROR.flat( k, true ) )
                    fault( s )
                end
            else
                s = string.format( "%s %s",
                                   I18N.fetch( "BadTemplateArg" ),
                                   ERROR.flat( k, true ) )
                fault( s )
            end
        end -- for k, v
    else
        fault( I18N.fetch( "BadTemplateArgs" ) )
    end
    return r
end  -- TEMPLATE.fill()



TEMPLATE.filter = function ( all, apart )
    -- Discard ignorable template parameters
    -- Precondition:
    --     all      -- table, with template parameters
    --     apart    -- string, with ignored template parameters
    -- Postcondition:
    --     Returns table, with template parameters
    local clear = { }
    local r = { }
    for i = 1, #apart do
        clear[ apart[ i ] ] = true
    end -- for i
    for k, v in pairs( all ) do
        if not clear[ k ] then
            r[ k ] = v
        end
    end -- for k, v
    return r
end  -- TEMPLATE.filter()



VALUES.features = function ( all, adjust, after )
    -- Convert list of items
    -- Precondition:
    --     all       -- string, with list of items
    --     adjust    -- string, with type of item
    --     after     -- string, with separator
    -- Postcondition:
    --     Returns table, with items, or not
    local scan     = mw.text.trim( all )
    local suitable = adjust[ 1 ]
    local r, sep, v, vals
    if after == "," then
        sep = "%s*,%s*"
    else
        sep = "[%s,|]+"
    end
    vals = mw.text.split( scan, sep )
    for i = 1, #vals do
        s = mw.text.split( vals[ i ] )
        if s == "" then
            v = false
        elseif suitable == "number" then
            v = VALUES.fine( s, "namespace" )
        else
            v = s
        end
        if v then
            r = r  or  { }
            table.insert( r, v )
        end
    end -- for i
    return r
end  -- VALUES.features()



VALUES.feed = function ( analyse, at, ask )
    -- Check value
    -- Precondition:
    --     analyse    -- string, with list
    --     at         -- string, with separator
    --     ask        -- true, requesting number elements, or not
    -- Postcondition:
    --     Returns table, or string with error, or nothing
    local r = mw.text.split( analyse, at )
    local s
    for i = #r, 1, -1 do
        s = mw.text.trim( r[ i ] )
        if s == "" then
            table.remove( r, i )
        elseif ask then
            if s:match( "^-?%d+$" ) then
                r[ i ] = tonumber( s )
            else
                r = { }
                break -- for i
            end
        end
    end -- for i
    if #r == 0 then
        r = false
    end
    return r
end  -- VALUES.feed()



VALUES.fine = function ( analyse, as )
    -- Check value
    -- Precondition:
    --     analyse    -- string or number, with value
    --     as         -- string, with key of pattern
    -- Postcondition:
    --     Returns true, if fine
    local s = type( analyse )
    local n, r
    if s == "string" then
        local req = VALUES.validate[ as ]
        s = type( req )
        if s == "table" then
            local oblis = req[ 1 ]
            local excls = req[ 2 ]
            if type( oblis ) == "table" then
                for i = 1, #oblis do
                    s = oblis[ i ]
                    if type( s ) == "string" then
                        r = analyse:match( string.format( "^%s$", s ) )
                        if r then
                            r = true
                            break -- for i
                        end
                    end
                end -- for i
            end
            if r and type( excls ) == "table" then
                for i = 1, #excls do
                    s = excls[ i ]
                    if type( s ) == "string"  and
                       analyse:match( s ) then
                        r = false
                        break -- for i
                    end
                end -- for i
            end
        elseif s == "string" then
        end
        if type( r ) ~= "boolean" then
            ERROR.fault( "VALUES.validate: " .. as,  "INTERNAL" )
        elseif r  and  as == "namespace" then
            n = tonumber( analyse )
        end
    elseif s == "number" then
        n = analyse
    end
    if n then
        if mw.site.namespaces[ n ] then
            r = n
        else
            r = false
        end
    end
    return r
end  -- VALUES.fine()



VALUES.full = function ()
    -- Retrieve list of permitted items
    -- Postcondition:
    --     tables with permitted items present
    for k, v in pairs( VALUES.arrays ) do
        if not VALUES.arrays[ k ] then
            VALUES.arrays[ k ] = COMMONS.fetch( k )  or  { }
        end
    end -- for k, v
end  -- VALUES.full()



MediaWikiGadgetDefinition.f = function ( arglist, frame )
    -- Perform standard functionality
    -- Precondition:
    --     arglist    -- table, with parameters
    --     frame      -- frame object, or not
    -- Postcondition:
    --     Returns mw.html, or not
    local r, s
    GLOBAL.frame( frame )
    if type( arglist.id ) == "string" then
        GLOBAL.id = mw.text.trim( arglist.id )
    end
    if type( arglist.template ) == "table" then
        GLOBAL.template = arglist.template
        if type( GLOBAL.template.ID ) == "string" then
            GLOBAL.id = mw.text.trim( GLOBAL.template.ID )
        end
        if type( arglist.IGNORE ) == "table" then
            GLOBAL.template = TEMPLATE.filter( GLOBAL.template,
                                               arglist.IGNORE )
        end
    end
    if GLOBAL.id == ""  or
       GLOBAL.id == "-" then
        GLOBAL.id = false
    end
    s = arglist.use
    if type( s ) == "string" then
        s = mw.text.trim( s )
        if s ~= ""  and
           s ~= "-" then
            ACTIVE.story = s
            GLOBAL.scope = "text"
            if not GLOBAL.id then
                GLOBAL.id = "*"
            end
        end
    end
    if GLOBAL.id then
        local tbl
        if type( arglist.strictRows ) == "boolean" then
            ROWS.lazy = arglist.strictRows
        end
        if type( arglist.Build ) == "string" then
            s = arglist.Build:upper()
            if s == "JSON"  or  s == "ROWS" then
                GLOBAL.syntax = s
            end
        end
        GLOBAL.syntax = GLOBAL.syntax  or  "HTML"
        if not GLOBAL.scope  and
           type( arglist.scope ) == "string" then
            s = mw.text.trim( arglist.scope )
            if s == "site"  or
               s == "global" then
                GLOBAL.scope = s
            elseif s == "" then
                GLOBAL.scope = "site"
            else
                if s:find( ":", 1, true ) then
                    local ns
                    ns, GLOBAL.scope = GLOBAL.focus( s )
                end
            end
            if not GLOBAL.scope then
                ERROR.fault( I18N.fetch( "InvalidScope" ),  "#invoke" )
            end
        end
        GLOBAL.scope = GLOBAL.scope or "site"
        if arglist.Export  and
           GLOBAL.scope == "site"  and
           GLOBAL.syntax == "HTML" then
            HTML.export = true
        end
        GLOBAL.first()
        if GLOBAL.scope ~= "global" then
            ACTIVE.first()
        end
        if GLOBAL.template then
            tbl = TEMPLATE.fill( GLOBAL.template )
            r   = HTML.folder( tbl )
        end
    else
        ERROR.fault( I18N.fetch( "IDmissing" ),  "#invoke" )
    end
    return r
end -- .f()



MediaWikiGadgetDefinition.params = function ()
    -- Communicate possible template parameters
    -- Postcondition:
    --     Returns mw.html <ul>
    local r = mw.html.create( "ul" )
    local e, o
    OPTIONS.first()
    for i = 2,  #OPTIONS.order do
        o = OPTIONS.order[ i ]
        e = mw.html.create( "li" )
                   :wikitext( o.sign )
        if not o.live then
            e:css( "text-decoration", "line-through" )
        end
        r:newline()
         :node( e )
    end -- for i
    return r
end -- .params()



MediaWikiGadgetDefinition.suggestedvalues = function ( array )
    local r
    if type( VALUES.arrays[ array ] ) == "boolean" then
        local vals = COMMONS.fetch( array )
        if type( vals ) == "table" then
            local sep = ""
            local e
            for i = 1, #vals do
                e = vals[ i ]
                if e[ 2 ] then
                    r   = string.format( "%s%s\"%s\"",
                                         r or "",
                                         sep,
                                         e[ 1 ] )
                    sep = ", "
                end
            end -- for i
        end
    end
    r = r or "ERROR"
    return string.format( "[ %s ]", r )
end -- .suggestedvalues()



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.f = function ( frame )
    local params = { }
    if frame.args.TEMPLATE == "1" then
        params.template = frame:getParent().args
        if frame.args.IGNORE  and
           frame.args.IGNORE ~="" then
            params.IGNORE = mw.text.split( frame.args.IGNORE,
                                           "%s*|%s*" )
        end
    else
        if frame.args.Use  and
           frame.args.Use ~=""  and
           frame.args.Use ~="-" then
            params.use = frame.args.Use
        else
            if frame.args.ExportLink == 1 then
                params.export = true
            end
            params.scope = frame.args.Scope
        end
    end
    if frame.args.StrictRows == "1" then
        params.strictRows = true
    end
    if frame.args.ID  and
       frame.args.ID ~= ""  and
       frame.args.ID ~= "-" then
        params.id = frame.args.ID
    end
    if frame.args.Build  and
       frame.args.Build ~="" then
        params.build = frame.args.Build
    end
    return MediaWikiGadgetDefinition.f( params, frame )  or  ""
end    -- p.f

p.params = function ( frame )
    return MediaWikiGadgetDefinition.params()
end    -- p.params

p.suggestedvalues = function ( frame )
    local s = frame.args[ 1 ]
    if s then
        s = mw.text.trim( s )
        if s == "" then
            s = false
        end
    end
    return MediaWikiGadgetDefinition.suggestedvalues( s )
end    -- p.suggestedvalues

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

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

return p

Content Disclaimer

Informasi ini disarikan dari Wikipedia dan disajikan kembali untuk tujuan edukasi. Konten tersedia di bawah lisensi CC BY-SA 3.0. Kami tidak bertanggung jawab atas ketidakakuratan data yang bersumber dari kontribusi publik tersebut.

  1. The information displayed on this website is sourced in part or in whole from Wikipedia and has been adapted for the purpose of restating it. We strive to provide accurate and relevant information, however:
  2. There is no guarantee of absolute accuracy. Wikipedia is an open, collaborative project that can be edited by anyone, so information is subject to change.
  3. It is not intended to constitute professional advice. The content displayed is for informational and educational purposes only. For important decisions (e.g., medical, legal, or financial), please consult a professional.
  4. Content copyright. Wikipedia is licensed under the Creative Commons Attribution-ShareAlike License (CC BY-SA). This means that content may be reused with appropriate attribution and shared under a similar license.
  5. Responsible use. Any risk arising from the use of information from this website is entirely the responsibility of the user.