Zum Inhalt springen

Modul:Zitation

aus Wikipedia, der freien Enzyklopädie
Dies ist eine alte Version dieser Seite, zuletzt bearbeitet am 20. August 2016 um 12:56 Uhr durch Doc Taxon (Diskussion | Beiträge) ("Band = 1" wurde schon immer zu "Bd. 1" und nicht zu "Band 1" (vielfach eingebundene Vorlagen nicht einfach ändern, sondern Änderung zuvor zur Diskussion stellen)). Sie kann sich erheblich von der aktuellen Version unterscheiden.
Vorlagenprogrammierung Diskussionen Lua Unterseiten
Modul Deutsch

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: 2021-05-25

Updating notwendig

(lokal: 2016-07-05)

Dieses Modul ist in über 300.000 Seiten eingebunden.

Jede Veränderung an dieser Seite zwingt die Server zum Neuaufbau aller dieser Seiten.

Die Entwicklung erfolgt in der BETA-Wikipedia und Änderungen werden dort zuerst erprobt, bevor sie hierher übertragen werden. Zwischenzeitliche Bearbeitungen hier werden dabei überschrieben.

--PerfektesChaos 18:07, 15. Mai 2016 (CEST)


local Serial = "2016-07-05"
--[=[
Zitation
]=]



Zitation = Zitation  or  { extern = false }
Zitation.serial = Serial
-- local globals
local Selbst      = "Modul:Zitation"
local FehlerTypen = { Intern    = { s = "Interner Fehler",
                                    k = "Intern" },
                      Entfernen = { s = "Veraltet, bitte entfernen" },
                      Format    = { s = "Parameterformat" },
                      Konflikt  = { s = "Parameterkonflikt",
                                    k = "Parameter" },
                      Modul     = { s = "Modul-Seite fehlt",
                                    k = "Intern" },
                      Name      = { s = "Schreibweise falsch",
                                    k = "Name" },
                      Pflicht   = { s = "Pflichtparameter fehlt",
                                    k = "Parameter" },
                      Vorlage   = { s = "Vorlagen-Seite fehlt",
                                    k = "Intern" },
                      Wert      = { s = "Ungültig",
                                    k = "Parameter" }
                    }
local KategorieBeginn  = "Wikipedia:Vorlagenfehler"
local Kategorien       = { Intern     = { s = "/Interner Fehler" },
                           Name       = { s = "/Parameterfehler" },
                           Parameter  = { s = "/Parameterfehler" },
                           arXiv      = { s = "Parameter:arXiv" },
                           bibcode    = { s = "Parameter:bibcode" },
                           Datum      = { s = "Parameter:Datum" },
                           DNB        = { s = "Parameter:DNB" },
                           DOI        = { s = "Parameter:DOI" },
                           ISBN       = { s = "Parameter:ISBN" },
                           ISSN       = { s = "Parameter:ISSN" },
                           JSTOR      = { s = "Parameter:JSTOR" },
                           LCCN       = { s = "Parameter:LCCN" },
                           OCLC       = { s = "Parameter:OCLC" },
                           PMID       = { s = "Parameter:PMID" },
                           Sprachcode = { s = "Parameter:Sprachcode" },
                           URN        = { s = "Parameter:URN" },
                           ZDB        = { s = "Parameter:ZDB" }
                         }
local DocTypes =
          { csv          = "[[CSV (Dateiformat)|CSV]]",
            djvu         = "[[DjVu]]",
            flash        = "[[Adobe Flash|Flash]]",
            gzip         = "[[gzip]]",
            mp3          = MP3,
            mpeg         = "[[Moving Picture Experts Group|MPEG]]",
            mpeg4        = "[[MPEG-4]]",
            msexcel      = "[[Microsoft Excel|MS Excel]]",
            mspowerpoint = "[[Microsoft PowerPoint|MS PowerPoint]]",
            msword       = "[[Microsoft Word|MS Word]]",
            pdf          = "PDF",
            postscript   = "[[PostScript]]",
            rtf          = "[[Rich Text Format]]",
            zip          = "[[ZIP-Dateiformat|ZIP]]" }
local Fehler = false
local Fun    = { }
local Resultat



-- Allgemeine Hilfsfunktionen ===========================================



local function faraway( assign, alien )
    -- Sprache zuweisen
    -- Parameter:
    --     assign  -- mw.html-Element
    --     alien   -- string mit Sprachcode, oder nil
    if alien  and  alien ~= "de" then
        assign:addClass( "lang" )
              :attr( { dir  = "auto",
                       lang = alien } )
    end
end -- faraway()



local function feed( area, access, about )
    -- Zugriff auf Parameterkomponente
    -- Parameter:
    --     area    -- string, mit Name der Parametergruppe
    --     access  -- string, mit Name der Komponente, oder nil
    --     about   -- true, for returning original parameter name
    -- Rückgabewert: Parameterwert, oder nil
    local e, r
    if not Zitation.o then
        Zitation.o = { }
    end
    e = Zitation.o[ area ]
    if e then
        if access then
            r = e[ access ]
            if type( r ) == "table" then
                if about then
                    r = r.s or "???????"
                else
                    r = r.v
                end
            end
        else
            r = e
        end
    end
    return r
end -- feed()



local function fehler( art, anzeige )
    -- Ein Fehler ist aufgetreten
    -- Parameter:
    --     art      -- string mit Schlüsselwort zum Typ
    --     anzeige  -- string mit Einzelheiten, oder nil
    local t
    if not Fehler then
        Fehler = FehlerTypen
    end
    t = Fehler[ art ]
    if t then
        if anzeige then
            local s = mw.text.nowiki( anzeige )
            if t.e then
                t.e = string.format( "%s; %s", t.e, s )
            else
                t.e = s
            end
        end
        if t.k then
            local wk = Kategorien[ t.k ]
            if wk then
                wk.e = true
            else
                Fehler.Intern.e     = "Wartungskat " .. wk
                Kategorien.Intern.e = true
            end
        end
    else
        Fehler.Intern.e     = string.format( "fehler(%s) %s",
                                             art, anzeige )
        Kategorien.Intern.e = true
    end
end -- fehler()



local function fehlerliste()
    -- Auflistung aller Fehlermeldungen und Kategorien
    -- Rückgabewert: string mit formatiertem Ergebnis
    local r = ""
    local s
    local t = mw.title.getCurrentTitle()
    if Fehler then
        local sep = ""
        for k, v in pairs( Fehler ) do
             if v.e then
                if v.s then
                    s = v.s .. ":"
                else
                    s = ""
                end
                r = string.format( "%s%s*** %s %s", r, sep, s, v.e )
                sep = " "
            end
        end -- for k, v
        r = "<br />" .. r
    end
    if t.namespace == 0    or
       ( t.namespace == 4   and
         t.text:sub( 1, 4 )  ==  "Lua/" ) then
        Selbst = feed( "leise", "Vorlage" )  or  Selbst
        for k, v in pairs( Kategorien ) do
            if v.e then
                if v.s:sub( 1, 1 ) == "/" then
                    s = Selbst
                else
                    s = ""
                end
                r = string.format( "%s[[Kategorie:%s/%s%s]]",
                                   r, KategorieBeginn, s, v.s )
            end
        end -- for k, v
    end
    return r
end -- fehlerliste()



local function fein( abtrennung, anhang )
    -- Ergänze Resultat um fertig formatierten Block
    -- Parameter:
    --     abtrennung  -- string mit vorangestelltem Separator
    --     anhang      -- string mit Textelement
    if anhang then
        Resultat = string.format( "%s%s%s",
                                  Resultat, abtrennung, anhang )
    end
end -- fein()



local function figures( a )
    -- Formatierte Aufzählung von Zahlen
    return a:gsub( "(%d)-(%d)", "%1&#8211;%2" )
            :gsub( "(%d)%s*(ff?)%.?", "%1&#8239;%2." )
            :gsub( "  +", " " )
            :gsub( " ", "&#160;" )
end -- figures()



local function findbar( assigned, about )
    -- Unauffindbare Namen (Personen) melden
    -- Parameter:
    --     assigned  -- Wert; zwei lateinische Buchstaben oder ein CJK
    --     about     -- Name des Parameters
    if not  mw.ustring.find( assigned, "%a.*%a" ) then
        local Text = Zitation.fetch( "Text" )
        if not Text.containsCJK then
            fehler( "Wert",  string.format( "'%s' zu kurz", about ) )
        end
    end
end -- findbar()



local function fire( art )
    -- Melde Kategorie an
    -- Parameter:
    --     art  -- string mit Schlagwort zum Typ
    local t = Kategorien[ art ]
    if t then
        t.e  =  true
    else
        fehler( "Intern",  "Kategorie:" .. art )
    end
end -- fire()



local function flat( adjust, anzahl, ascii )
    -- Komma-separierte Aufzählung begrenzen
    -- Parameter:
    --     adjust  -- string, mit Aufzählung
    --     anzahl  -- number, mit Anzahl erlaubter Elemente
    --     ascii   -- true, für ASCII-Auslassung
    -- Rückgabewert: string mit gleichem oder gekürztem Ergebnis
    local i = 1
    local n = 0
    local r = adjust
    while i do
        i = r:find( ",", i, true )
        if i then
            n = n + 1
            if n == anzahl then
                r = r:sub( 1, i )
                if ascii then
                    r = r .. " ..."
                else
                    r = r .. "&#160;&#8230;"   -- nbsp hellip
                end
                break -- while
            end
            i = i + 1
        end
    end    -- while i
    return r
end -- flat()



local function foreign( area, access )
    -- Sprachcodes zuweisen
    -- Parameter:
    --     area    -- string, mit Name der Parametergruppe
    --     access  -- string, mit Name der Komponente
    local r = feed( area, access )
    if r then
        local Multilingual = Zitation.fetch( "Multilingual" )
        local s            = Multilingual.format( r, "-",
                                                  false, false, false,
                                                  Zitation.frame,
                                                  "[, ]", " " )
        local parts        = mw.text.split( s or "", " " )
        local lapsus
        for i = 1, #parts do
            if not Multilingual.getName( parts[ i ] ) then
                lapsus = true
                break -- for i
            end
        end -- for i
        if lapsus then
            fehler( "Wert",  "Sprachcode=" .. r )
            fire( "Sprachcode" )
            s = r:lower():match( "^(%l%l%l?)-" )
            if s then
                Zitation.fill( area, access, s )
            end
        elseif s ~= r then
            Zitation.fill( area, access, s )
            s = string.format( "%s: '%s' statt '%s' verwenden",
                               "Sprachcode", s, r )
            fehler( "Format", s )
            r = s
        end
    end
    return r
end -- foreign()



local function framedTemplate( access, args )
    -- Vorlage einbinden
    -- Parameter:
    --     access  -- Name der Vorlage
    --     args    -- table mit Parameterliste
    -- Rückgabewert: string mit expandierter Vorlage
    if not Zitation.frame then
        Zitation.frame = mw.getCurrentFrame()
    end
    return Zitation.frame:expandTemplate{ title = access, args = args }
end -- framedTemplate()



local function future( ask )
    -- Liegt Datum in der Zukunft?
    -- Parameter:
    --     ask  -- table oder string, mit Datum
    -- Rückgabewert: true, wenn ask ungültig oder in der Zukunft
    local r = true
    local DateTime, datum
    if not Zitation.heute then
        DateTime       = Zitation.fetch( "DateTime" )
        Zitation.heute = DateTime()
    end
    if type( ask ) == "string" then
        DateTime = Zitation.fetch( "DateTime" )
        datum    = DateTime( ask )
    else
        datum = ask
    end
    if type( datum ) == "table" then
        r = ( Zitation.heute < datum )
    end
    return r
end -- future()



-- Spezielle Werte ======================================================



local function Abrufdatum( abruf )
    -- Gib behauptetes Abrufdatum zurück
    -- Parameter:
    --     abruf  -- table oder string, mit Datum
    local o = abruf
    local r
    if type( o ) == "string" then
        local DateTime = Zitation.fetch( "DateTime" )
        o = DateTime( o )
    end
    if type( o ) == "table" then
        if not future( o ) then
            local s = o:format( "ISO" )
            r = o:format( "T._Monat JJJJ", "de" )
            if abruf ~= s then
                fehler( "Format",
                        string.format( "'%s'=%s soll sein: %s",
                                       feed( "www", "Abruf", true ),
                                       abruf,
                                       s ) )
                fire( "Datum" )
            end
        end
    end
    if not r then
        r = abruf
        fehler( "Wert",
                string.format( "'%s'=%s",
                               feed( "www", "Abruf", true ),
                               abruf ) )
        fire( "Datum" )
    end
    return "abgerufen am " .. r
end -- Abrufdatum()



local function Herausgeber( apply, above, ahead )
    -- Analysiere Herausgeber und formatiere ihn, mit Klammerzusatz
    -- Parameter:
    --     access  -- string mit Herausgeber
    --     above   -- true: innerhalb Klammerebene
    --     ahead   -- true: vorangestellt statt Klammerzusatz
    -- Rückgabewerte:  -- string
    local pat  = "^([^%(%[]+)[%(%[]((%w+)%.?)[%)%]](.*)$"
    local scan = apply
    local seek = "|hg|hsg|hrsg|hrsgg|herausgeber|ed|eds|editor|editors|"
    local story = ""
    local r, s, start, sub, suffix
    while true do
        start, sub, s, suffix = mw.ustring.match( scan, pat )
        if s then
            if seek:find( string.format( "|%s|", s:lower() ) ) then
                story = story .. mw.text.trim( start )
            elseif suffix:sub( 1, 1 ) == "|"   and
                sub:sub( 1, -1 ) == ")" then
                story = string.format( "%s%s%s",
                                       story, start, sub )
            else
                story = string.format( "%s%s[%s]",
                                       story, start, s )
            end
            scan = suffix
        else
            break -- while
        end
    end -- while
    r = story .. scan
    sub, suffix = mw.ustring.match( r, "^(%w+%.?)%s*(.+)$" )
    if sub == "Hg." then
        -- (Verwechslungsgefahr bei abgekürztem Vornamen)
        r = mw.text.trim( suffix )
    elseif sub then
        seek = "|hrsg|hrsgg|herausgegeben|"
        if seek:find( string.format( "|%s|", sub:lower() ) ) then
            r = mw.text.trim( suffix )
            sub, suffix = mw.ustring.match( r, "^(vo?n?.?)%s*(.+)$" )
            if sub == "von"  or  sub == "v." then
                r = mw.text.trim( suffix )
            end
        end
    end
    if r ~= apply then
        fehler( "Wert", "Herausgeber mit unnötigem Zusatz" )
    end
    findbar( r, "Hrsg" )
    if ahead then
        r = "Hrsg.: " .. r
    else
        if above then
            r = r .. " [Hrsg.]"
        else
            r = r .. " (Hrsg.)"
        end
    end
    return r
end -- Herausgeber()



Fun.arXiv = function ( access )
    -- Analysiere arXiv-ID und gib sie als formatierten Link zurück
    local arXiv   = Zitation.fetch( "arXiv", "Vorlage:arXiv" )
    local details = arXiv.fair( access )
    if not details.legal then
        fehler( "Wert", "'arXiv'" )
        fire( "arXiv" )
    end
    arXiv.features( { showArticle = "arXiv" } )
    return arXiv.format( details )
end -- Fun.arXiv()



Fun.bibcode = function ( access )
    -- Analysiere bibcode-ID und gib sie als formatierten Link zurück
    local bibcode = Zitation.fetch( "bibcode", "Vorlage:bibcode" )
    local r       = bibcode.format{ access }
    if not r:find( "//", 1, true ) then
        fehler( "Wert",  "'bibcode' =" .. access )
        fire( "bibcode" )
    end
    return r
end -- Fun.bibcode()



Fun.DOI = function ( access )
    -- Analysiere DOI und gib sie als formatierten Link zurück
    local URIutil = Zitation.fetch( "URIutil" )
    local r       = URIutil.linkDOI( access )
    local s       = "Digital Object Identifier"
    if r then
        r = string.format( "[[%s|doi]]:%s", s, r )
    else
        r = string.format( "[[%s|DOI]]:%s%s",
                           s,  access,  Zitation.fault( "(?!)", true ))
        fehler( "Wert", "'DOI'" )
        fire( "DOI" )
    end
    return r
end -- Fun.DOI()



Fun.DNB = function ( access )
    -- Analysiere DNB und gib sie formatiert zurück
    local URIutil = Zitation.fetch( "URIutil" )
    local r
    if URIutil.isDNBvalid( access ) then
        r = URIutil.linkDNBopac( access, false, true, true )
    else
        local s = "Deutsche Nationalbibliothek"
        fehler( "Wert", "'DNB'" )
        fire( "DNB" )
        r = string.format( "[[%s|DNB]] %s%s",
                           s,  access,  Zitation.fault( "(?!)", true ))
    end
    return r
end -- Fun.DNB()



Fun.ISSN = function ( access, allow )
    -- Analysiere ISSN und gib sie formatiert zurück
    --     allow    -- true: permit invalid check digit
    local URIutil = Zitation.fetch( "URIutil" )
    if allow or URIutil.isISSNvalid( access ) then
        r = URIutil.linkISSN( access, allow, true, true )
    else
        local s = "International Standard Serial Number"
        fehler( "Wert", "'ISSN'" )
        fire( "ISSN" )
        r = string.format( "[[%s|ISSN]]&#160;%s%s",
                           s,  access,  Zitation.fault( "(?!)", true ))
    end
    return r
end -- Fun.ISSN()



Fun.ISSNfalsch = function ( access )
    -- Analysiere formal falsche ISSN und gib sie formatiert zurück
    return Fun.ISSN( access, true )
end -- Fun.ISSNfalsch()



Fun.JSTOR = function ( access )
    -- Analysiere JSTOR (stable) und gib sie formatiert zurück
    -- i: Volume
    local legal
    if access:find( "/", 1, true ) then
        local URIutil = Zitation.fetch( "URIutil" )
        legal = URIutil.isDOI( access )
    else
        legal = access:match( "^i?[1-9][0-9]*$" )
    end
    if not legal then
        fehler( "Wert", "'JSTOR'" )
        fire( "JSTOR" )
    end
    return framedTemplate( "JSTOR", { access } )
end -- Fun.JSTOR()



Fun.LCCN = function ( access )
    -- Analysiere LCCN und gib sie formatiert zurück
    local URIutil = Zitation.fetch( "URIutil" )
    local see = "Library of Congress Control Number"
    local r = string.format( "[[%s|LCCN]]&#160;", see )
    if URIutil.isLCCN( access ) then
        r = r .. URIutil.linkLCCN( access, "-" )
    else
        fehler( "Wert", "'LCCN'" )
        fire( "LCCN" )
        r = string.format( "%s%s%s",
                           r,  access,  Zitation.fault( "(?!)", true ))
    end
    return r
end -- Fun.LCCN()



Fun.Lizenznummer = function ( access )
    -- Gib DDR-Lizenznummer formatiert zurück
    return "[[Lizenznummer]] " .. access
end -- Fun.Lizenznummer()



Fun.OCLC = function ( access )
    -- Analysiere OCLC und gib sie formatiert zurück
    local r
    if access:match( "^[1-9][0-9]*$" ) then
        r = framedTemplate( "OCLC", { access } )
    else
        fehler( "Wert", "'OCLC'" )
        fire( "OCLC" )
        r = string.format( "OCLC %s", access )
    end
    return r
end -- Fun.OCLC()



Fun.PMC = function ( access )
    -- Analysiere PMC und gib sie formatiert zurück
    local r
    if access:match( "^[1-9][0-9]*$" ) then
        r = framedTemplate( "PMC", { access } )
    else
        fehler( "Wert", "'PMC'" )
        fire( "PMID" )    -- Ja, PMID-Experte betreut auch PMC
        r = string.format( "PMC %s", access )
    end
    return r
end -- Fun.PMC()



Fun.PMID = function ( access )
    -- Analysiere PMID und gib sie formatiert zurück
    if not access:match( "^[1-9][0-9]*$" ) then
        fehler( "Wert", "'PMID'" )
        fire( "PMID" )
    end
    return string.format( "PMID %s", access )
end -- Fun.PMID()



Fun.URN = function ( access )
    -- Analysiere URN und gib sie formatiert zurück
    local URIutil = Zitation.fetch( "URIutil" )
    local r = URIutil.uriURN( "urn:" .. access )
    if not r:find( "//", 1, true ) then
        fehler( "Wert",  "'URN'=" .. access )
        fire( "URN" )
    end
    return r
end -- Fun.URN()



Fun.ZDB = function ( access )
    -- Analysiere ZDB und gib sie formatiert zurück
    local URIutil = Zitation.fetch( "URIutil" )
    if URIutil.isDNBvalid( access )  or
       URIutil.isDNBvalid( access:gsub( "-", "" ) ) then
        r = framedTemplate( "ZDB", { access:upper() } )
    else
        local s = "Zeitschriftendatenbank"
        fehler( "Wert", "'ZDB'" )
        fire( "ZDB" )
        r = string.format( "[[%s|ZDB]] %s%s",
                           s,  access, Zitation.fault( "(?!)", true ))
    end
    return r
end -- Fun.ZDB()



local function redundanz( analyse )
    -- Prüfe Angabe auf Redundanz mit anderen Parametern
    -- Parameter:
    --     analyse  -- string
    local urlPats = { arXiv   = "//arxiv%.org/abs/",
                      bibcode = "//adsabs%.harvard%.edu/",
                      DNB     = "//portal%.dnb%.de/opac.+query=%d+X?$",
                      DOI     = "//dx%.doi%.org/10%.",
                      JSTOR   = "jstor%.org/stable/",
                      PMC     = "ncbi%.nlm%.nih%.gov/pmc/articles/PMC%d",
                      PMID    = "ncbi%.nlm%.nih%.gov/pubmed/%d",
                      URN     = "//nbn-resolving%.de/urn:" }
    for k, v in pairs( urlPats ) do
        if analyse:match( v ) then
            local s = string.format( "%s '%s=' %s",
                                     "Statt URL sollte etwas wie",
                                     k,
                                     "angegeben werden" )
            fehler( "Konflikt", s )
        end
    end -- for k, v
    if analyse:find( "title=\"ctx_ver=Z39.88-2004", 1 , true ) then
        fehler( "Konflikt", "Verschachtelte Zitationsvorlagen" )
    end
end -- redundanz()



local function bandNummer( area )
    -- Formatiere Angaben von Band und/oder Nummer
    -- Parameter:
    --     area  -- string, mit Name der Parametergruppe print/serie
    -- Rückgabewert: string mit Inhalt, oder nil
    local sB = feed( area, "Band" )
    local sN = feed( area, "Nummer" )
    local r
    if sB then
        if sB:match( "^%d+$" ) then
            r = "Bd.&#8239;" .. sB
        else
            r = sB
        end
    end
    if sN then
        if r then
            r = r .. ", "
        else
            r = ""
        end
        if not mw.ustring.find( sN, "%a" ) then
            sN = "Nr.&#8239;" .. sN
        end
        r = r .. sN
    end
    return r
end -- bandNummer()



local function Kapitel( a )
    -- Formatiere Kapitelangabe
    local r = a
    if a:match( "^%d+$" ) then
        r = "Kap.&#8239;" .. a
    end
    return r
end -- Kapitel()



local function ArtikelNr( aN, aS )
    -- Analysiere ArtikelNr
    if aS then
        fehler( "Konflikt", "Seitenzahl redundant wenn ArtikelNr" )
    end
    if not aN:match( "^%d+$" ) then
        fehler( "Wert", "'ArtikelNr'=" .. aN )
    end
end -- ArtikelNr()



local function Seiten( a )
    -- Analysiere Seitenzahl und formatiere
    local s, seiten = a:match( "^(%w+%.?)%s*(.+)$" )
    if s then
        local seek = "|s.|ss.|seite|seiten|page|pages|p.|pp.|"
        if seek:find( string.format( "|%s|", s:lower() ) ) then
            fehler( "Wert", "Seitenzahl mit unnötigem Zusatz" )
            seiten = mw.text.trim( seiten )
        else
            seiten = a
        end
    else
        seiten = a
    end
    return "S.&#8239;" .. figures( seiten )
end -- Seiten()



local function Spalten( a )
    -- Analysiere Spaltenangabe und formatiere
    local s, sp = a:match( "^(%w+%.?)%s*(.+)$" )
    if s then
        local seek = "|sp.|spalte|spalten|"
        if seek:find( string.format( "|%s|", s:lower() ) ) then
            fehler( "Wert", "Spaltenangabe mit unnötigem Zusatz" )
            sp = mw.text.trim( sp )
        else
            sp = a
        end
    else
        sp = a
    end
    return "Sp.&#8239;" .. figures( sp )
end -- Spalten()



local function Werktitel( a, amend, alien, abschluss )
    -- Formatiere einen Werktitel, das Sammelwerk, ggf. Reihe
    -- Parameter:
    --     a          -- string
    --     amend      -- string mit Ergänzung, oder nil
    --     alien      -- string mit Sprachcode, oder nil
    --     abschluss  -- true: Satzendezeichen sicherstellen
    -- Rückgabewert: string mit formatiertem Werktitel
    local Text  = Zitation.fetch( "Text" )
    local cite = mw.html.create( "cite" )
    local sep
    local r
    cite:css( "font-style", "italic" )
    cite:wikitext( Text.uprightNonlatin( a ) )
    faraway( cite, alien )
    r = tostring( cite )
    if amend then
        local s
        if Text.sentenceTerminated( a ) then
            sep = ""
        else
            sep = "."
        end
        r = string.format( "%s%s %s", r, sep, amend )
    end
    if abschluss  and
       ( ( not amend  and  not Text.sentenceTerminated( a ) )
         or   ( amend  and  not Text.sentenceTerminated( amend ) ) ) then
        r = r .. "."
    end
    return r
end -- Werktitel()



-- Einzelblöcke der Darstellung =========================================



local function resourceMeta( anfang )
    -- Ergänze Resultat um für einen Online-Abruf wichtigen Informationen
    --     anfang  -- boolean
    --                * true   -- runde Klammern
    --                            um .Format und .KBytes gesetzt
    --                * false  -- eckige Klammern
    --                            um .Format und .KBytes und .Abruf
    -- Rückgabewert: string mit führendem " " und Inhalt, oder ""
    local r = ""
    if feed( "www" ) then
        local sURL     = feed( "www", "URL" )
        local sWeblink = feed( "www", "Weblink" )
        local some     = ( sURL or sWeblink )
        local sAbruf   = feed( "www", "Abruf" )
        local sFormat  = feed( "www", "Format" )
        local sKBytes  = feed( "www", "KBytes" )
        if some then
            local sep = ""
            some = some:lower() .. " "
            if sFormat then
                local s = sFormat:upper()
                if s ~= "HTML" then
                    if s:find( "PDF%-?" ) then
                        r = "pdf"
                    else
                        r = sFormat
                    end
                end
            elseif some:find( "%Wpdf%W" ) then
                r = "pdf"
            end
            if r:lower() == "pdf"  and
               sWeblink  and
               some:find( " .*pdf" ) then
                r = ""
            end
            if r == "" then
                sKBytes = false
            else
                r   = DocTypes[ r:lower() ]  or  r
                sep = "; "
            end
            if sKBytes then
                local scan = "^(%d+[,%.]?%d*)%s*(%a*)$"
                local size, suffix
                sKBytes = sKBytes:gsub( "&nbsp;", " " )
                                 :gsub( "&#8239;", " " )
                                 :gsub( "&thinsp;", " " )
                size, suffix = sKBytes:match( scan )
                if size then
                    local n
                    size = size:gsub( "%.", "" )
                    if size:find( "," ) then
                        n    = tonumber( size:gsub( ",", "." ), 10 )
                        size = string.format( "%1.1d", n )
                    else
                        n = tonumber( size )
                    end
                    if suffix  and
                       suffix:lower():match( "^mi?b$" ) then
                        n      = n * 1000
                        suffix = "kb"
                    end
                    if not suffix   or   suffix == ""   or
                       ( suffix  and
                         suffix:lower():match( "^ki?b%l*$" ) ) then
                        if n > 1000 then
                            n      = math.ceil( n * 0.01 ) * 0.1
                            size   = string.format( "%.1f", n )
                            suffix = "MB"
                        else
                            suffix = "kB"
                        end
                    end
                    sKBytes = string.format( "%s&#8239;%s",
                                             size:gsub( "%.", "," ),
                                             suffix )
                end
                r = string.format( "%s%s%s", r, sep, sKBytes )
                sep = "; "
            end
            if not anfang and sAbruf and sWeblink then
                r = string.format( "%s%s%s",
                                   r, sep, Abrufdatum( sAbruf ) )
            end
            if r ~= "" then
                if anfang then
                    sep = " (%s)"
                else
                    sep = " &#91;%s&#93;"
                end
                r = string.format( sep, r )
            end
            redundanz( some )
        elseif sFormat or sKBytes or sAbruf then
            fehler( "Konflikt",
                    "Dateiformat/Größe/Abrufdatum nur wenn Weblink" )
        end
    end
    return r
end -- resourceMeta()



local function autorHrsg()
    -- Ergänze Resultat um Personen zu Beginn der Zitation
    local sAutor = feed( "bas", "Autor" )
    local sHrsg  = feed( "bas", "Hrsg" )
    local sTyp   = feed( "leise", "Typ" )
    local lead
    if sTyp  and  sTyp ~= "wl" then
        fehler( "Wert",
                "'Typ' zurzeit nur 'Typ=wl' (Werkliste) unterstützt" )
        r = "wl"
    end
    if sHrsg then
        lead = ( not feed( "bas", "Werk" ) )
        findbar( sHrsg, "Hrsg" )
    end
    if sAutor or lead then
        local list = false
        if sAutor then
            if sTyp ~= "wl" then
                fein( "", sAutor )
                list = true
            end
            if type( sAutor ) == "table" then
                sAutor = Zitation.citePerson( sAutor, false )
            end
            findbar( sAutor, "Autor" )
            if sHrsg  and  not feed( "bas", "Titel" ) then
                fehler( "Konflikt",
                        "Gleichzeitig 'Autor' und 'Herausgeber' nur wenn auch 'Titel'" )
            end
        else
            fein( "",  Herausgeber( sHrsg ) )
            list = true
        end
        if list then
            fein( ": ", "" )
        end
    end
end -- autorHrsg()



local function erstAusgabe()
    -- Erstausgabe
    -- Rückgabewert: string
    local sJahr   = feed( "ed1", "Jahr" )
    local sOrt    = feed( "ed1", "Ort" )
    local sVerlag = feed( "ed1", "Verlag" )
    local r       = "Erstausgabe: "
    local sep
    if sVerlag then
        r   = r .. sVerlag
        sep = ","
        if feed( "bas", "Verlag" ) == sVerlag then
            fehler( "Konflikt",
                    string.format( "'%s' hat gleichen Wert wie '%s'",
                                   feed( "ed1", "Verlag", true ),
                                   feed( "bas", "Verlag", true ) ) )
        end
    else
        sep = ""
    end
    if sOrt then
        r   = string.format( "%s%s %s", r, sep, sOrt )
        sep = " "
        -- bas.Verlag und ed1.Verlag dürfen in derselben Stadt sein
    end
    if sJahr then
        local jahr
        if sJahr:match( "^%d%d%d%d$" ) then
            if not future( sJahr ) then
                jahr = tonumber( sJahr )
            end
        end
        if jahr then
            local datum = feed( "bas", "Datum" )
            r = string.format( "%s%s %s", r, sep, sJahr )
            if type( datum ) == "table"   and
               datum.year   and   datum.year < jahr then
                fehler( "Konflikt",
                        string.format( "'%s' nach Neuausgabe",
                                       feed( "ed1", "Jahr", true ) ) )
            end
        else
            fehler( "Wert",
                    string.format( "'%s'=%s",
                                   feed( "ed1", "Jahr", true ),
                                   sJahr ) )
            fire( "Datum" )
        end
    end
    return r
end -- erstAusgabe()



local function originalPublikation()
    -- Originalpublikation; zulässige Parameterlogik noch unklar
    -- Rückgabewert: string mit Inhalt, oder false
    local r
    if feed( "orig" ) then
        local sTitel      = feed( "orig", "Titel" )
        local sTranslator = feed( "orig", "Translator" )
        if sTitel then
            local sprache = foreign( "orig", "Sprache" )
            local sOrt    = feed( "orig", "Ort" )
            local sJahr   = feed( "orig", "Jahr" )
            r = Werktitel( sTitel, false, sprache, true )
            if sOrt then
                r = string.format( "%s %s", r, sOrt )
            end
            if sJahr then
                r = string.format( "%s %s", r, sJahr )
                if sJahr:match( "^%d%d%d%d$" ) then
                    if future( sJahr ) then
                        fehler( "Wert", "Originaljahr in der Zukunft" )
                    else
                        local jahr = tonumber( sJahr )
                        local datum = feed( "bas", "Datum" )
                        if type( datum ) == "table"   and
                           datum.year   and   datum.year < jahr then
                            fehler( "Konflikt",
                                    "Originaljahr nach Neuausgabe" )
                        end

                    end
                else
                    fehler( "Wert", "Originaljahr ist keine Jahreszahl" )
                end
            end
            if sOrt or sJahr then
                r = r .. "."
            end
            if sprache then
                local Multilingual = Zitation.fetch( "Multilingual" )
                local s = Multilingual.format( sprache, "de", "m",
                                               false, false,
                                               Zitation.frame,
                                               " ", ", " )
                if s then
                    sprache = s
                elseif sprache:match( "^%l%l%l?$" ) then
                    fehler( "Wert",
                            "Unbekannter Sprachcode=" .. sprache )
                    fire( "Sprachcode" )
                    sprache = string.format( "<code>%s</code>", sprache )
--              elseif sprache:match( "^%l.+[ ,;/]" ) then
--                  fehler( "Wert",
--                          "Original-Sprachcode seltsam: " .. sprache )
                end
            else
                sprache = "Originaltitel"
            end
            r = string.format( "%s: %s", sprache, r )
            if sTranslator then
                r = string.format( "%s Übersetzt von %s",
                                   r, sTranslator )
            end
        elseif not sTranslator then
            fehler( "Pflicht", "Originaltitel fehlt" )
        end
    end
    return r
end -- originalPublikation()



local function titel()
    -- Ergänze Resultat um Titel
    local sTitel = feed( "bas", "Titel" )
    local sWerk  = feed( "bas", "Werk" )
    if sTitel then
        local stop = ""
        local sReihe    = feed( "serie", "Reihe" )
        local sTitelErg = feed( "bas",   "TitelErg" )
        local sURL      = feed( "www",   "URL" )
        local s = Werktitel( sTitel,
                             false,
                             feed( "bas", "Sprache" ),
                             sWerk  or  not sReihe  or  sTitelErg )
        local Text
        if sURL then
            local URLutil = Zitation.fetch( "URLutil" )
            if URLutil.isResourceURL( sURL ) then
                s    = s:gsub( "%[", "&#91;" )
                        :gsub( "%]", "&#93;" )
                sURL = sURL:gsub( "%[", "%5B" )
                           :gsub( "%]", "%5D" )
                s    = string.format( "[%s %s]%s",
                                      sURL, s, resourceMeta( true ) )
            else
                fehler( "Wert", "URL" )
            end
        end
        if sTitelErg then
            Text = Zitation.fetch( "Text" )
            if ( sWerk  or  not sReihe )   and
               not Text.sentenceTerminated( sTitelErg ) then
                stop = "."
            end
            s = string.format( "%s %s%s", s, sTitelErg, stop )
        end
        fein( "", s )
        if not sWerk then
            local sAutor = feed( "bas", "Autor" )
            local sHrsg  = feed( "bas", "Hrsg" )
            if sAutor and sHrsg then
                if stop == "" then
                    Text = Zitation.fetch( "Text" )
                    if not Text.sentenceTerminated( Resultat ) then
                        stop = "."
                    end
                elseif sTitelErg then
                    stop = ""
                end
                stop = stop .. " "
                fein( stop,  Herausgeber( sHrsg, false, true ) .. "." )
            end
        end
    else
        if sWerk then
            fehler( "Pflicht", "Kein 'Titel'" )
        else
            fehler( "Pflicht", "Weder 'Titel' noch sonstiges Werk" )
        end
    end
end -- titel()



local function werk()
    -- Ergänze Resultat um Sammelwerk usw.
    local sWerk = feed( "bas", "Werk" )
    if sWerk then
        local start = " In: "
        local sHrsg = feed( "bas", "Hrsg" )
        local s     = Werktitel( sWerk,
                                 feed( "bas", "WerkErg" ),
                                 feed( "bas", "Sprache" ),
                                 not feed( "serie", "Reihe" ) )
        if sHrsg then
            s = string.format( "%s: %s",  Herausgeber( sHrsg ),  s )
        end
        if Resultat == "" then
            start = "In: "
        end
        fein( start, s )
    end
end -- werk()



local function auflage()
    -- Ergänze Resultat um Auflage
    local sAuflage = feed( "print", "Auflage" )
    if sAuflage then
        local s = sAuflage:gsub( "Auflage$",   "Auflage")
                          :gsub( "[eE]d%.?$",  "ed." )
                          :gsub( "[eE]dition", "ed." )
                          :gsub( "[éÉ]d%.?$",  "éd." )
                          :gsub( "[éÉ]dition", "éd." )
        if s:match( "^%d+$" ) then
            s = s .. "."
        end
        if not ( s:find( "Aufl", 1, true )  or
                 s:find( "[eé]d" ) ) then
            s = s .. " Auflage"
        end
        if not s:match( "%.$" ) then
            s = s .. "."
        end
        fein( " ", s )
    end
end -- auflage()



local function reihe()
    -- Ergänze Resultat um Angaben zur Reihe
    if feed( "serie" ) then
        local sReihe = feed( "serie", "Reihe" )
        if sReihe then
            local sBN   = bandNummer( "serie" )
            local sHrsg = feed( "serie", "Hrsg" )
            local s     = Werktitel( sReihe,
                                     false,
                                     feed( "bas", "Sprache" ),
                                     sBN )
            if sHrsg then
                s = string.format( "%s: %s",
                                   Herausgeber( sHrsg, true ),  s )
            end
            if sBN then
                s = string.format( "%s %s", s, sBN )
            end
            fein( " ",  string.format( "(=&#160;%s).", s ) )
        else
            local scream
            local flop = function ( au )
                             if feed( "serie", au ) then
                                 local s = feed( "serie", au, true )
                                 if scream then
                                     scream = scream .. ", "
                                 else
                                     scream = ""
                                 end
                                 scream = string.format( "%s'%s'",
                                                         scream, s )
                             end
                         end -- flop()
            flop( "Hrsg" )
            flop( "Band" )
            flop( "Nummer" )
            if scream then
                -- Muss in dieser Konstellation eigentlich immer sein
                fehler( "Pflicht",
                        scream .. " nur wenn Reihe angegeben" )
            end
        end
    end
end -- reihe()



local function bibliografischeAngaben()
    -- Ermittle die Aufzählung bibliografische Angaben
    -- Rückgabewert: string mit Inhalt, oder false
    local r       = ""
    local sep     = ""
    local datum   = feed( "bas", "Datum" )
    local sVerlag = feed( "bas", "Verlag" )
    local s, sOrt
    if feed( "print" ) then
        sOrt = feed( "print", "Ort" )
        s    = bandNummer( "print" )
        if s then
            r = s
            if s:find( "%.$" ) then
                sep = " "
            elseif sVerlag or sOrt then
                sep = ". "
            else
                sep = ", "
            end
        end
    end
    if sVerlag then
        r = string.format( "%s%s%s", r, sep, sVerlag )
        sep = ", "
    end
    if sOrt  and
       not ( feed( "id", "ISSN" )  or  feed( "id", "ZDB" ) ) then
        s = sOrt:gsub( "[,/; ]+$", "" )
        r = string.format( "%s%s%s", r, sep, s )
        if s ~= sOrt then
            Zitation.fill( "print", "Ort", s )
        end
        sep = ", "
    end
    if datum then
        local o = datum
        if type( datum ) == "string" then
            local DateTime = Zitation.fetch( "DateTime" )
            o = DateTime( datum )
        end
        if type( o ) == "table"  and  o.year then
            Zitation.fill( "bas", "Datum", o )
            if future( o ) then
                o = false
            elseif feed( "id", "ISBN" ) then
                s = tostring( o.year )
            else
                s = o:format( "T._Monat JJJJ" )
            end
        else
            o = false
        end
        if not o then
            if type( datum ) == "string" then
            --  s = "Datum: " .. datum    -- LEGACY
                s = datum
            else
                s = "Datum??"
            end
            fehler( "Wert", s )
            fire( "Datum" )
        end
        if sOrt then
            sep = " "
        end
        r = string.format( "%s%s%s", r, sep, s )
        sep = ", "
    end
    if feed( "id" ) then
        local lazy        = false    -- gültige ISBN bekannt
        local sID         = feed( "id", "ID" )
        local sISBN       = feed( "id", "ISBN" )
        local sISBNfalsch = feed( "id", "ISBNfalsch" )
        local sISBNdefekt = feed( "id", "ISBNdefekt" )
        local fiddler     =
                  function( at )
                      local s = feed( "id", at )
                      if s then
                          if lazy  and
                             not  ( at == "ISSN"  or
                                    at == "ISSNfalsch" ) then
                              s = "redundant, da ISBN gegeben"
                              s = string.format( "'%s' %s",
                                                 feed( "id", at, true ),
                                                 s )
                              fehler( "Konflikt", s )
                          else
                              s   = Fun[ at ]( s )
                              r   = string.format( "%s%s%s",
                                                   r, sep, s )
                              sep = ", "
                          end
                      end
                  end
        if sISBN and sISBNfalsch then
            s = string.format( "'%s' und '%s' %s",
                               feed( "id", "ISBN", true ),
                               feed( "id", "ISBNfalsch", true ),
                               "nicht gleichzeitig angeben" )
            fehler( "Konflikt", s )
        elseif sISBN or sISBNfalsch then
            local mode
            if sISBNfalsch then
                mode = -1
            end
            s, lazy = Zitation.ISBN( sISBN or sISBNfalsch,  mode )
            r = string.format( "%s%s%s", r, sep, s )
            sep = ", "
        end
        if sISBNdefekt then
            s = Zitation.ISBN( sISBNdefekt, #sISBNdefekt )
            r = string.format( "%s%s%s", r, sep, s )
            sep = ", "
        end
        fiddler( "ISSN" )
        fiddler( "ISSNfalsch" )
        fiddler( "DNB" )
        fiddler( "LCCN" )
        fiddler( "Lizenznummer" )
        fiddler( "OCLC" )
        fiddler( "ZDB" )
        if sID then
            r = string.format( "%s%s%s", r, sep, sID )
            sep = ", "
            redundanz( sID )
        end
    end
    if feed( "fragment" ) then
        local sArtikelNr  = feed( "fragment", "ArtikelNr" )
        local sFundstelle = feed( "fragment", "Fundstelle" )
        local sKapitel    = feed( "fragment", "Kapitel" )
        local sSeiten     = feed( "fragment", "Seiten" )
        local sSpalten    = feed( "fragment", "Spalten" )
        if sKapitel then
            r = string.format( "%s%s%s", r, sep, Kapitel( sKapitel ) )
            sep = ", "
        end
        if sSeiten then
            r = string.format( "%s%s%s", r, sep, Seiten( sSeiten ) )
            sep = ", "
        end
        if sSpalten then
            r = string.format( "%s%s%s", r, sep, Spalten( sSpalten ) )
            sep = ", "
        end
        if sArtikelNr then
            ArtikelNr( sArtikelNr, sSeiten )
            r = string.format( "%s%s%s", r, sep, sArtikelNr )
            sep = ", "
        end
        if sFundstelle then
            r = string.format( "%s%s<span style='%s'>%s</span>",
                               r,
                               sep, "white-space:nowrap", sFundstelle )
            sep = ", "
        end
    end
    if feed( "id" ) then
        local docCodes   = { "DOI",
                             "PMID",
                             "PMC",
                             "arXiv",
                             "bibcode",
                             "JSTOR",
                             "URN" }
        local sign
        for i = 1, #docCodes do
            s    = docCodes[ i ]
            sign = feed( "id", s )
            if sign then
                r   = string.format( "%s%s%s", r, sep, Fun[ s ]( sign ) )
                sep = ", "
            end
        end -- for i
    end
    if r == "" then
        r = false
    end
    return r
end -- bibliografischeAngaben()



local function klammerInhalt()
    -- Inhalt der Klammer am Ende der bibliografischen Angaben
    -- Rückgabewert: string mit Inhalt, oder false
    local r          = ""
    local sep        = ""
    local sKommentar = feed( "bas",   "Kommentar" )
    local sOrig      = originalPublikation()
    local sprache    = feed( "bas",   "Sprache" )
    local sUmfang    = feed( "print", "Umfang" )
    if sprache  and  sprache ~= "de" then
        local Multilingual = Zitation.fetch( "Multilingual" )
        sprache = Multilingual.format( sprache, "de", "m",
                                       false, false,
                                       Zitation.frame,
                                       " ", ", " )
        if sprache:find( "^%a+er " ) then
            -- Rechtschreibduden, K90
            sprache = mw.ustring.lower( mw.ustring.sub( sprache, 1, 1 ) )
                      .. mw.ustring.sub( sprache, 2 )
        end
        r   = string.format( "%s%s%s", r, sep, sprache )
        sep = ", "
    end
    if sUmfang then
        if not mw.ustring.find( sUmfang, "%a" ) then
            -- "14, 234"
            sUmfang = string.format( "%s&#160;S.", sUmfang )
        end
        r   = string.format( "%s%s%s", r, sep, sUmfang )
        sep = ", "
    end
    if feed( "www" ) then
        local sWeblink = feed( "www", "Weblink" )
        if sWeblink then
            local WLink = Zitation.fetch( "WLink" )
            local show  = WLink.getWeblink( sWeblink )
            r   = string.format( "%s%s%s%s",
                                 r,
                                 sep,
                                 show:gsub( " www%d*%.", " " ),
                                 resourceMeta( false ) )
            sep = " "
            if sWeblink:find( "</cite>", 1, true )   or
               sWeblink:find( "class=\"cite\"", 1, true ) then
                fehler( "Konflikt",
                        "Zitationsvorlage rekursiv eingebunden" )
                fire( "Parameter" )
                -- Langfristig wirkungslos; greift nur vorübergehend
                -- wenn IQ noch reine Vorlage,
                -- oder cite als reine Vorlage.
                -- Nach vollständiger Umsetzung
                -- sperrt sich aber das System,
                -- weil Modul:Zitation
                -- dann rekursiv aufgerufen werden würde.
            end
        elseif feed( "www", "URL" ) then
            local sAbruf = feed( "www", "Abruf" )
            if sAbruf then
                r   = string.format( "%s%s%s",
                                     r, sep, Abrufdatum( sAbruf ) )
                sep = ", "
            end
        else
            -- ERROR  URL ./. Weblink   "Konflikt"
        end
    end
    if sOrig then
        r = string.format( "%s%s%s", r, sep, sOrig )
        if feed( "orig", "Translator" ) then
            sep = ", "
        else
            sep = " "
        end
    end
    if feed( "ed1" ) then
        r   = string.format( "%s%s%s", r, sep, erstAusgabe() )
        sep = ", "
    end
    if sKommentar then
        r = string.format( "%s%s%s", r, sep, sKommentar )
        redundanz( sKommentar )
    end
    if r == "" then
        r = false
    end
    return r
end -- klammerInhalt()



local function endBlock()
    -- Ergänze Resultat um bibliografische Angaben, Klammer sowie Zitat.
    -- Komma-getrennte Aufzählung, die mit Punkt abgeschlossen wird,
    -- oder Zitat wird nachgestellt.
    local sZitat = feed( "bas", "Zitat" )
    local sep    = ""
    local s      = bibliografischeAngaben()
    if s then
        fein( " ", s )
        sep = "."
    end
    s = klammerInhalt()
    if s then
        if Resultat:find( "%)$" ) then
            -- Irgendwas zuvor endet auf eine runde Klammer.
            sep = " &#8211; ("
        else
            sep = " ("
        end
        fein( sep,  s .. ")" )
        sep = "."
    end
    if sep ~= "" then
        -- Aufzählung enthält zumindest ein Element
        if sZitat then
            fein( ":", "" )
        elseif not Resultat:match( "%.$" ) then
            fein( ".", "" )
        end
    end
    if sZitat then
        local sprache = feed( "bas", "Sprache" )
        local Text    = Zitation.fetch( "Text" )
        local story   = Text.quoteUnquoted( sZitat, sprache )
        if sprache  and  sprache ~= "de" then
            local q = mw.html.create( "span" )
            faraway( q, sprache )
            q:wikitext( story )
            story = tostring( q )
        end
        fein( " ", story )
    end
end -- endBlock()



local function coins()
    -- Ergänze Resultat um COinS, wenn gewünscht
    local COinS = feed( "coins" )
    if COinS then
        local std = "book"
        local pars
        if type( COinS ) == "table" then
            pars = COinS
        elseif feed( "bas" ) then
            local datum    = feed( "bas",      "Datum" )
            local sAutor   = feed( "bas",      "Autor" )
            local sKapitel = feed( "fragment", "Kapitel" )
            local sTitel   = feed( "bas",      "Titel" )
            local sWerk    = feed( "bas",      "Werk" )
            local stick
            pars = { }
            if sWerk then
                if feed( "print",    "Nummer" )  or
                   feed( "id",       "ISSN" )  or
                   feed( "id",       "ISSNfalsch" )  or
                   feed( "id",       "ZDB" )  or
                   feed( "fragment", "ArtikelNr" ) then
                    pars.genre  = "journal"
                    pars.jtitle = sWerk
                    std         = "journal"
                else
                    pars.genre  = "book"
                    pars.btitle = sWerk
                end
                pars.atitle = sTitel
            elseif sKapitel then
                pars.genre  = "bookitem"
                pars.btitle = sTitel
                pars.atitle = sKapitel
            else
                pars.genre  = "book"
                pars.btitle = sTitel
            end
            if type( datum ) == "table" then
                pars.date = datum:format( "ISO" )
            end
            if sAutor then
                if type( sAutor ) == "table" then
                    stick = Zitation.citePerson( sAutor, true )
                else
                    pars.au = flat( sAutor, 3, true )
                end
            end
            pars.pub   = feed( "bas",      "Verlag" )
            pars.pages = feed( "fragment", "Seiten" )
            if feed( "id" ) then
                pars.isbn = feed( "id", "ISBN" )  or
                            feed( "id", "ISBNfalsch" )
                            feed( "id", "ISBNfalsch" )
                pars.issn = feed( "id", "ISSN" )  or
                            feed( "id", "ISSNfalsch" )
                pars.oclc = feed( "id", "OCLC" )
                pars.doi  = feed( "id", "DOI" )
                pars.pmc  = feed( "id", "PMC" )
                pars.pmid = feed( "id", "PMID" )
            end
            if feed( "print" ) then
                pars.edition = feed( "print", "Auflage" )
                pars.issue   = feed( "print", "Nummer" )
                pars.place   = feed( "print", "Ort" )
                pars.volume  = feed( "print", "Band" )
            end
            pars.series = feed( "serie", "Reihe" )
        end
        if pars then
            fein( "",  Zitation.COinS( pars, false, stick ),  std )
        end
    end
end -- coins()



-- Exportierte Funktionen ===============================================



Zitation.fault = function ( a, always )
    -- Formatiere Fehler mit class=error; teils ausgeblendet
    -- Parameter:
    --     a       -- string, mit Fehlermeldung
    --     always  -- true, wenn nicht zu unterdrücken
    -- Rückgabewert:
    --     HTML-Element
    local e = mw.html.create( "span" )
    e:addClass( "error" )
     :wikitext( a )
    if not always  and
       feed( "leise", "leiser" )  and
       mw.site.server == "//de.wikipedia.org" then
        if not Zitation.frame then
            Zitation.frame = mw.getCurrentFrame()
        end
        if Zitation.frame:preprocess( "{{REVISIONID}}" ) ~= "" then
            e:addClass( "Zitationsfehler" )
             :css( "display", "none" )
        end
    end
    return tostring( e )
end -- Zitation.fault()



Zitation.fetch = function ( assigned, acquire )
    -- Binde Modul ein
    -- Parameter:
    --     assigned  -- string mit Name
    --                  "arXiv"
    --                  "bibcode"
    --                  "DateTime"
    --                  "Multilingual"
    --                  "Text"
    --                  "URIutil"
    --                  "URLutil"
    --                  "WLink"
    --     acquire   -- string mit abweichendem Modulnamen, oder false
    -- Rückgabewert: table des Moduls
    -- error: Modul nicht gefunden
    local r
    if Zitation.extern then
        r = Zitation.extern[ assigned ]
    else
        Zitation.extern = { }
    end
    if not r then
        local s = assigned
        if acquire then
            s = acquire
        end
        local lucky, g = pcall( require, "Module:" .. s )
        if type( g ) == "table" then
            r = g[ assigned ]()
            Zitation.extern[ assigned ] = r
        else
            fehler( "Modul", g )
            error( string.format( "Zitation.fetch(%s) %s", s, g ) )
        end
    end
    return r
end -- Zitation.fetch()



Zitation.figure = function ( adjust )
    -- Bilde Zahlenwert
    -- Parameter:
    --     adjust  -- Wert beliebigen Typs
    -- Rückgabewert:
    --     Numerischer Wert, notfalls 0
    local r
    local s = type( adjust )
    if s == "string" then
        r = tonumber( adjust ) or 0
    elseif s == "number" then
        r = adjust
    else
        r = 0
    end
    return r
end -- Zitation.figure()



Zitation.fill = function ( area, access, assign, alias )
    -- Parameterkomponente zuweisen
    -- Parameter:
    --     area    -- string, mit Name der Parametergruppe
    --     access  -- string, mit Name der Komponente
    --     assign  -- Parameterwert
    --     alias   -- string, mit Name des Benutzerparameters, oder nil
    if not Zitation.o then
        Zitation.o = { }
    end
    if type( Zitation.o[ area ] ) ~= "table" then
        Zitation.o[ area ] = { }
    end
    Zitation.o[ area ][ access ] = { s = alias    or
                                         string.format( "%s.%s",
                                                        area, access ),
                                     v = assign }
end -- Zitation.fill()



Zitation.filler = function ( args, assign )
    -- Parameterkomponenten zuweisen
    -- Parameter:
    --     args    -- Zfilter.object,
    --                mit Zuweisungen nach Vorlagenparametername
    --     assign  -- table, mit Transformation in neutrales Datenmodell
    local g, r, value
    if not Zitation.o then
        Zitation.o = { }
    end
    for k, v in pairs( assign ) do
        value = args{ k }
        if value then
            g = v[ 1 ]
            if not Zitation.o[ g ] then
                Zitation.o[ g ] = { }
            end
            Zitation.o[ g ][ v[ 2 ] ] = value
        end
    end -- for k, v
end -- Zitation.filler()



Zitation.filter = function ( args, allowed )
    -- Analysiere Argumentenliste und gleiche mit erlaubten Namen ab
    -- Parameter:
    --     args     -- table, mit aktuellen Werten
    --     allowed  -- table, mit erlaubten Namen, zugewiesen:
    --                        true   -- Nur diese Namensvariante bekannt
    --                        table  -- Namensvariationen
    --                                  Jeder Wert:
    --                                  true   -- unerwünscht, Meldung
    --                                  table  -- Details
    --                                  table  -- low=true: Keine Meldung
    -- Rückgabewerte:
    --     table, mit gefilterten Werten, nach Parametername
    --            Zfilter object
    --            Jede Komponente:
    --                 index-Zugriff:
    --                     string, mit Parameterwert, kein leerer string
    --                 get-Zugriff:
    --                     table, mit
    --                            s=Orginal-Parametername
    --                            v=Parameterwert, nicht leer
    local signatur = "__Zfilter"
    local meta     = { }
    local r        = { [ signatur ] = { } }
    local discard  = false
    local doubled  = false
    local un       = false
    local d, lapsus
    meta.__call     = function ( self, arglist )
                          -- Antwort auf:  Tabelle{ ... }
                          return self[ signatur ][ arglist[ 1 ] ]
                      end
    meta.__index    = function ( self, access )
                          -- Antwort auf:  ... = Tabelle[x]
                          local e = self[ signatur ][ access ]
                          if type( e ) == "table" then
                              e = e.v
                          end
                          return e
                      end
    meta.__newindex = function ( self, access, assign )
                       -- Antwort auf:  Tabelle[x] = ...
                          local put = assign
                          if assign  and
                             ( type( assign ) ~= "table"   or
                               not assign.v ) then
                              put = { s=access, v=assign }
                          end
                          self[ signatur ][ access ] = put
                          return
                      end
    setmetatable( r, meta )
    for s, v in pairs( args ) do
        d = allowed[ s ]
        if d then
            lapsus = false
            if type( d ) == "table" then
                for dk, dv in pairs( d ) do
                    if args[ dk ] then
                        if not doubled then
                            doubled = { }
                        end
                        if not doubled[ dk ] then
                            doubled[ tostring( s ) ] = dk
                        end
                    end
                end
            else
                d = false
            end
        elseif type( s ) == "string" then
            if d == false then
                if not discard then
                    discard = { }
                end
                table.insert( discard, s )
            else
                lapsus = true
            end
        else
            lapsus = true
            if v then
                if mw.text.trim( v ) == "" then
                    fehler( "Format", "Pipe '|' zu viel" )
                end
                v = false
            end
            s = tostring( s )
        end
        if lapsus then
            if not un then
                un = { }
            end
            un[ s ] = true
        end
        if v == "" then
            v = false
        end
        if v then
            r[ s ] = v
            if v:find( "'''", 1, true )   and
               type( s ) == "string" then
                fehler( "Wert",
                        string.format( "'%s' mit Wikisyntax", s ) )
            end
        end
    end -- for s, v
    if un then
        local down      = { }
        local scream    = false
        local undesired = false
        local unknown   = false
        local light, s, sa
        for k, v in pairs( allowed ) do
            s = mw.ustring.lower( k )
            down[ s ] = { standard=k }
            if type( v ) == "table" then
                for ka, va in pairs( v ) do
                    sa = mw.ustring.lower( ka )
                    if type( va ) == "table" then
                        va.standard = k
                    else
                        va = { standard=k }
                    end
                    down[ sa ] = va
                end
            end
        end -- for k, v
        for k, v in pairs( un ) do
            if type( k ) == "string" then
                s = mw.ustring.lower( k )
                d = down[ s ]
            else
                d = false
            end
            if d then
                if type( d ) == "table" then
                    light = d.low
                    s     = d.standard
                else
                    light = false
                end
                if r[ s ] then
                    if not doubled then
                        doubled = { }
                    end
                    if not doubled[ s ] then
                        doubled[ k ] = s
                    end
                else
                    r[ s ] = r{ k }
                    r[ k ] = nil
                    if not light then
                        if not undesired then
                            undesired = { }
                        end
                        undesired[ k ] = s
                    end
                end
            else
                if not unknown then
                    unknown = { }
                end
                unknown[ k ] = true
            end
        end -- for k, v
        if unknown then
            down = { }
            for k, v in pairs( allowed ) do
                if type( v ) == "table" then
                    sa = mw.ustring.lower( k )
                    for ka, va in pairs( v ) do
                        sa = mw.ustring.lower( ka )
                        if type( va ) == "table" then
                            va.standard = k
                        else
                            va = { standard=k }
                        end
                        down[ sa ] = va
                    end
                end
            end -- for k, v
            for k, v in pairs( unknown ) do
                if type( k ) == "string" then
                    s = mw.ustring.lower( k )
                    d = down[ s ]
                    if d then
                        if type( d ) == "table" then
                            light = d.low
                        else
                            light = false
                        end
                        s = d.standard
                        if r[ s ] then
                            if not doubled then
                                doubled = { }
                            end
                            doubled[ k ] = s
                        else
                            r[ s ] = r{ k }
                            r[ k ] = nil
                            if not light then
                                if not undesired then
                                    undesired = { }
                                end
                                undesired[ k ] = d.standard
                            end
                        end
                    else
                        if scream then
                            scream = scream .. ", "
                        else
                            scream = "Unbekannte Parameter: "
                        end
                        scream = scream .. k
                    end
                end
            end -- for k, v
            fehler( "Konflikt", scream )
            scream = false
        end
        if undesired then
            for k, v in pairs( undesired ) do
                if scream then
                    scream = scream .. ", "
                else
                    scream = ""
                end
                scream = string.format( "%s '%s' ist '%s'",
                                        scream, k, v )
            end -- for k, v
            fehler( "Name", scream )
            scream = false
        end
    end
    if doubled then
        for k, v in pairs( doubled ) do
            if scream then
                scream = scream .. ","
            else
                scream = "Parameterwerte gedoppelt: "
            end
            scream = string.format( "%s '%s' ./. '%s'",
                                    scream, k, v )
        end -- for k, v
        fehler( "Konflikt", scream )
        scream = false
    end
    if discard then
        for k, v in pairs( discard ) do
            fehler( "Entfernen",  v .. "=" )
        end -- for k, v
    end
    return r
end -- Zitation.filter()



Zitation.format = function ()
    -- Generiere Zitation
    -- Rückgabewert:
    --     1  -- string mit Vorlagenresultat
    --     2  -- string mit Fehlermeldung(en) und -kategorien, oder false
    local s
    Resultat = ""
    foreign( "bas", "Sprache" )
    autorHrsg()
    titel()       -- Schließt mit Punkt etc.
    werk()        -- Schließt mit Punkt etc.
    reihe()
    auflage()     -- Schließt mit Punkt
    endBlock()    -- Schließt mit Punkt
    Resultat = Resultat:gsub( "&#8239;",
                              "<span width='.25em'>&#160;</span>")
    coins()
    s = fehlerliste()
    if s == "" then
        s = false
    end
    return Resultat, s
end -- Zitation.format()



Zitation.COinS = function ( args, assign, already )
    -- Create string with COinS <span>
    -- Parameter:
    --     args     -- table, with COinS components
    --     assign   -- optional string, with ID
    --     already  -- optional string, with preformatted &sequence
    -- Returns HTML element string
    local Text  = Zitation.fetch( "Text" )
    local WLink = Zitation.fetch( "WLink" )
    local rft   = { }
    local site  = mw.site.server:gsub( "^%l*:?//", "" )
    local s, sub, v
    if assign then
        sub = assign
    else
        if args.genre then
            sub = args.genre
        else
            sub = "book"
        end
    end
    if args.isbn then
        args.isbn = args.isbn:gsub( "-", "" ):upper()
    end
    s = string.format( "%s%s%s",
                       "ctx_ver=Z39.88-2004",
                       "&rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3A",
                       sub )
    if not args.genre then
        s = s .. "&rft.genre=book"
    end
    if type( assign ) == "string" then
        sub = assign
    else
        sub = mw.title.getCurrentTitle().fullText
    end
    s = string.format( "%s&rfr_id=info:sid/%s:%s",
                       s,
                       site,
                       mw.uri.encode( sub ) )
    if already then
        s = s .. already
    end
    for k, v in pairs( args ) do
       table.insert( rft, k )
    end -- for k, v
    table.sort( rft )
    for i = 1, #rft do
        sub = rft[ i ]
        v   = args[ sub ]
        if type( v ) == "table" then
            if type( v.tostring ) == "function" then
                v = v.tostring()
            end
        end
        if type( v ) == "string" then
            v = mw.uri.encode( WLink.getPlain( Text.getPlain( v ) ) )
                             :gsub( "%%E2%%80%%93", "-" )
            s = string.format( "%s&rft.%s=%s", s, sub, v )
        end
    end -- for i
    s = string.format( "<span class=\"Z3988\" title=\"%s\" %s>%s</span>",
                       s,
                       "style='display:none'",
                       "&#160;" )
    return s
end -- Zitation.COinS()



Zitation.ISBN = function ( access, accept, alert )
    -- Create string with formatted ISBN
    -- Parameter:
    --     access   -- string, with presumable ISBN
    --     accept   -- optional number, whether invalid data is permitted
    --                  0, nil   -- require valid ISBN
    --                 -1        -- ignore invalid check digit
    --                 other     -- other, e.g. number of digits
    --     alert    -- optional string, with maintenance category title
    -- Returns:
    --     1  -- string, for display
    --     2  -- true, if conditions not matched
    local URIutil = Zitation.fetch( "URIutil" )
    local mode  = accept or 0
    local isbn, lapsus, legal, lethal, r
    if mode == -1 then
        legal, isbn = URIutil.isISBN( access )
        if legal then
            if URIutil.isISBNvalid( access ) then
                fehler( "Wert", "'ISBN' ist nicht formal falsch" )
            else
                lapsus = true
            end
        end
    elseif mode == 0 then
        legal, isbn = URIutil.isISBNvalid( access )
    else
        legal, isbn  = URIutil.isISBN( access )
        if isbn == -1 then
            lapsus = true
            legal  = true
            lethal = true
        end
    end
    if legal then
        if lethal then
            r = URIutil.linkISBN( access, true, true, true, alert )
        else
            r = "ISBN " .. URIutil.formatISBN( access, isbn )
        end
        if lapsus then
            local plus = mw.html.create( "small" )
            local show, story
            if lethal then
                show  = "defekt"
                story = "WP:ISBNdefekt"
            else
                show  = "formal falsch"
                story = "WP:ISBNformalFalsch"
            end
            if story then
                show = string.format( "[[%s|%s]]", story, show )
            end
            show = string.format( "(%s)", show )
            plus:addClass( "ISBN-bad-code" )
                :css( "white-space", "nowrap" )
                :wikitext( show )
            r = string.format( "%s&#160;%s", r, tostring( plus ) )
        end
    else
        r = string.format( "ISBN %s%s",
                           access,
                           Zitation.fault( "(?!)", true ) )
        fehler( "Wert", "'ISBN'" )
        fire( "ISBN" )
    end
    return r, legal
end -- Zitation.ISBN()



-- Export ===============================================================

local p = { }

p.Endpunkt = function ( frame )
    -- LEGACY für Vorlage:Internetquelle
    local r = ""
    local s = frame.args.titel
    if s then
        local Text = Zitation.fetch( "Text" )
        if Text.sentenceTerminated( s ) then
            r = ""
        else
            r = "."
        end
    end
    return r
end -- p.Endpunkt


p.TitelFormat = function ( frame )
    -- LEGACY für Vorlage:Internetquelle
    local r = ""
    local s = frame.args.titel
    if s then
        local Text = Zitation.fetch( "Text" )
        if Text.sentenceTerminated( s ) then
            r = s
        else
            r = s .. "."
        end
        r = string.format( "<i>%s</i>", r )
    end
    return r
end -- p.TitelFormat


p.COinS_Template = function ( frame )
    local l, r = pcall( Zitation.COinS, frame:getParent().args )
    return r
end -- p.COinS_Template


p.ISBN = function ( frame )
    local mode = frame.args[ 2 ]
    if mode then
        mode = tonumber( mode )
    end
    if frame.args.template then
        Selbst = frame.args.template
    end
    local l, r = pcall( Zitation.ISBN,
                        frame.args[ 1 ],
                        mode,
                        frame.args.link )
    return r
end -- p.ISBN


function p.failsafe()
    return Zitation.serial
end


p.Zitation = function ()
    return Zitation
end -- p.Zitation

return p