Wiki/Modul:Zitation

Aus BiodynWiki

local Serial = "2019-01-17" --[=[ 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" },
                     Problem   = { s = "Parameterproblem" },
                     Abruf     = { s = "Abrufdatum mangelhaft",
                                   k = "Abruf" },
                     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" },
                         Abruf      = { s = "/Abrufdatum" },
                         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",
           DJVU         = "DjVu",
           FLASH        = "Flash",
           GZIP         = "gzip",
           MIDI         = "Musical Instrument Digital Interface",
           MP3          = "MP3",
           MPEG         = "MPEG",
           MPEG4        = "MPEG-4",
           MSEXCEL      = "MS Excel",
           MSPOWERPOINT = "MS PowerPoint",
           MSWORD       = "MS Word",
           PDF          = "PDF",
           POSTSCRIPT   = "PostScript",
           RAR          = "RAR",
           REAL         = "RealPlayer",
           RTF          = "Rich Text Format",
           WINDOWSMV    = "WMV",
           ZIP          = "ZIP" }

local URLunwanted = {

                 ["//arxiv%.org/abs/"]                      = "arXiv",
                 ["//adsabs%.harvard%.edu/"]                = "bibcode",
                 ["//portal%.dnb%.de/opac.+query=%d+X?$"]   = "DNB",
                 ["//doi%.org/10%."]                        = "DOI",
                 ["//dx%.doi%.org/10%."]                    = "DOI",
                 ["jstor%.org/pss/"]                        = "JSTOR",
                 ["jstor%.org/stable/"]                     = "JSTOR",
                 ["www%.worldcat%.org/oclc"]                = "OCLC",
                 ["ncbi%.nlm%.nih%.gov/pmc/articles/PMC%d"] = "PMC",
                 ["ncbi%.nlm%.nih%.gov/pmc/articles/pmc%d"] = "PMC",
                 ["ncbi%.nlm%.nih%.gov/pubmed/%d"]          = "PMID",
                 ["//nbn%-resolving%.de/urn:"]              = "URN",
                 ["//urn%.nb%.no/urn:nbn:"]                 = "URN",
                 ["//urn%.kb%.se/resolve%?urn="]            = "URN" }

local Fehler = false local Fun = { } local KBytesMax = 100 local Resultat local Silent = false local Spacer


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


local function faced( ahead, after )

   -- Zwei Elemente durch schmalen Abstand verbinden
   -- Parameter:
   --     ahead  -- string, mit erstem Element
   --     after  -- string, mit zweitem Element
   -- Rückgabewert: string
   local e
   if not Spacer then
       e = mw.html.create( "span" )
                  :css( { display = "inline-block",
                          width   = ".2em" } )
                  :wikitext( " " )
       Spacer = tostring( e )
   end
   return string.format( "%s%s%s", ahead, Spacer, after )

end -- faced()


local function facet( ahead, after )

   -- Zwei Elemente durch schmalen umbruchgeschützten Abstand verbinden
   -- Parameter:
   --     ahead  -- string, mit erstem Element
   --     after  -- string, mit zweitem Element
   -- Rückgabewert: string
   local e = mw.html.create( "span" )
                    :css( "white-space", "nowrap" )
                    :wikitext( faced( ahead, after ) )
   return tostring( e )

end -- facet()


local function fading()

   -- Ausblende-Modus feststellen
   -- Rückgabewert: true, wenn im Ansicht-Modus (kein Bearbeitungsmodus)
   if not Silent then
       if not Zitation.frame then
           Zitation.frame = mw.getCurrentFrame()
       end
       Silent = Zitation.frame:preprocess( "107" )
   end
   return ( Silent ~= "" )

end -- fading()


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
               s = string.format( "*** %s %s", s, v.e )
               if not v.k  and  fading() then
                   local e = mw.html.create( "span" )
                   e:wikitext( s )
                    :addClass( "Zitationsfehler Zitationswartung" )
                    :css( "display", "none" )
                   s = tostring( e )
               end
               r = string.format( "%s%s%s", r, sep, s )
               sep = " "
           end
       end -- for k, v
   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",
                                  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 figure( a )

   -- Ist das eine Zahl aus arabischen oder römischen Ziffern?
  return a:match( "^[0-9./%-:IVX]+$" )   or
         a:match( "^C*[LXVI]+$" )   or
         not mw.ustring.find( a, "%a" )

end -- figure()


local function figures( a )

   -- Formatierte Aufzählung von Zahlen
   local i = true
   local r = a
   local j, k, s, suffix
   while i do
       i, j, suffix = r:match( "^(%d+)%-(%d+)(.*)$" )
       if i then
           s = ""
       else
           s, i, j, suffix = r:match( "^(.*[^0-9]?)(%d+)%-(%d+)(.*)$" )
       end
       if i then
           if tonumber( i ) < tonumber( j ) then
               k = 8211
           else
               k = 45
           end
           r = string.format( "%s%s&#%d;%s%s",
                              s, i, k, j, suffix )
       end
   end -- while
   return r:gsub( "  +", " " )
           :gsub( "(%d)%s*(ff?)%.?", "%1 %2." )
           :gsub( " ", " " )
           :gsub( "(%d+) (ff?%.)", faced )

end -- figures()


local function findbar( assigned, about )

   -- Unauffindbare Namen (Personen) melden
   -- Parameter:
   --     assigned  -- string; zwei lateinische Buchstaben oder ein CJK
   --     about     -- string mit Parametername
   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 .. " …"   -- 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 == "Undetermined" then
       fehler( "Wert", "Sprache" )
       fire( "Sprachcode" )
   elseif 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
           local say = string.format( "%s: '%s' statt '%s' verwenden",
                                      "Sprachcode", s, r )
           r = s
           Zitation.fill( area, access, r )
           fehler( "Format", say )
       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 )
       if r then
           local karenz = Zitation.heute:future( "15 days" )
           if karenz > datum then
               r = false
           end
       end
   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 not o.dom or not o.month then
               fehler( "Abruf", "Abrufdatum soll taggenau sein" )
           elseif 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  or  r:match( "%)$" ) then
       fehler( "Wert", "Herausgeber mit problematischem 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()


local function Ortsname( analyse, area, access )

   -- Analysiere einen Ortsnamen
   -- Parameter:
   --     analyse  -- string, mit Ortsname
   --     area     -- string, mit Name der Parametergruppe
   --     access   -- string, mit Name der Komponente
   local s = analyse:gsub( "&#%x+;", "" )
   if s:find( "%d%d%d%d" ) then
       fehler( "Wert",
               string.format( "'%s' %s (%s)",
                              feed( area, access, true ),
                              "mit verdächtiger Ziffernfolge",
                              "Jahreszahl, Postleitzahl" ) )
   end

end -- Ortsname()


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( "doi:%s", s, r )
   else
       r = string.format( "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( "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( "ISSN %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 JSTOR = Zitation.fetch( "JSTOR" )
   local r, URIutil
   if access:find( "/", 1, true ) then
       URIutil = Zitation.fetch( "URIutil" )
   end
   r = JSTOR.feasible( access, "stable", URIutil )
   if r then
       r = JSTOR.format( r, "stable", false, URIutil )
   else
       fehler( "Wert", "'JSTOR'" )
       fire( "JSTOR" )
       r = string.format( "JSTOR:%s%s",
                          access,  Zitation.fault( "(?!)", true ))
   end
   return r

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( "LCCN ", 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 start, s = access:match( "^(%a%a%a)%s*(%d+)$" )
   local r
   if start and start:upper() == "PMC" then
       fehler( "Wert", "'PMC' vor Nummer unerwünscht" )
   else
       s = access
   end
   if s:match( "^[1-9][0-9]*$" ) then
       r = framedTemplate( "PMC", { s } )
   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( "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
   for k, v in pairs( URLunwanted ) do
       if analyse:match( k ) then
           local s = string.format( "%s '%s=' %s",
                                    "Statt URL sollte etwas wie",
                                    v,
                                    "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 lead, r
   if sB then
       sB = sB:gsub( " ", " " )
              :gsub( "&#%x+;", " " )
              :gsub( " ", " " )
              :gsub( "%s%s+", " " )
       if sB:sub( 1, 5 ) == "Band " then
           sB   = sB:sub( 6 )
           lead = true
       elseif sB:sub( 1, 3 ) == "Bd." then
           sB   = mw.text.trim( sB:sub( 4 ) )
           lead = true
       end
       if figure( sB ) then
           r = facet( "Band", sB )
       else
           if lead then
               sB = "Band " .. sB
           end
           r = sB:gsub( "(Band) (%d+)", facet )
       end
   end
   if sN then
       lead = false
       sN = sN:gsub( " ", " " )
              :gsub( "&#%x+;", " " )
              :gsub( " ", " " )
              :gsub( "%s%s+", " " )
       if sN:sub( 1, 7 ) == "Nummer " then
           sN   = sN:sub( 8 )
           lead = true
       elseif sN:sub( 1, 3 ) == "Nr." then
           sN   = mw.text.trim( sN:sub( 4 ) )
           lead = true
       end
       if figure( sN ) then
           sN = facet( "Nr.", sN )
       else
           if lead then
               sN = "Nr. " .. sN
           end
           sN = sN:gsub( "(Nr%.) (%d+)", facet )
       end
       if r then
           r = string.format( "%s, %s", r, sN )
       else
           r = sN
       end
   end
   return r

end -- bandNummer()


local function Kapitel( a )

   -- Formatiere Kapitelangabe
   local r = a
   if a:match( "^%d+$" ) then
       r = facet( "Kap.", 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*(.+)$" )
   local r
   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
   if #seiten > 50 then
       -- URL? Google-Buch?
       local shrink = string.format( "[%%-0-9,;/ f%%.%s]",
                                     mw.ustring.char( 8211 ) )
       s = mw.ustring.gsub( seiten, shrink, "" )
       if #s > 10 then
           fehler( "Wert", "Seitenzahl unerklärlich lang" )
           r = a
       end
   end
   if not r then
       r = facet( "S.",  figures( seiten ) ):gsub( "%.$",
                                                   "." )
   end
   return r

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  facet( "Sp.", figures( sp ) ):gsub( "%.$",
                                               "." )

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" )
       :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
               local docTypes = mw.text.split( r:upper(), " " )
               local s
               r   = ""
               sep = ""
               for i = 1, #docTypes do
                   s   = docTypes[ i ]
                   r   = string.format( "%s%s%s",
                                        r,  sep,  DocTypes[ s ] or s )
                   sep = " "
               end -- for i
               sep = "; "
           end
           if sKBytes then
               local scan  = "^(%d+[,%.]?%d*)%s*(%a*)$"
               local large = sKBytes:find( "[sic!]", 3, true )
               local lot, size, suffix
               sKBytes = sKBytes:gsub( " ", " " )
                                :gsub( " ", " " )
                                :gsub( "&#x0*[aA]0;", " " )
                                :gsub( " ", " " )
                                :gsub( "&#x0*202[fF];", " " )
                                :gsub( " ", " " )
               if large then
                   sKBytes = sKBytes:gsub( "%s*%[sic!%]%s*$", "" )
               end
               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 then
                       if suffix == "" then
                           suffix = false
                       else
                           suffix = suffix:lower()
                           if suffix:match( "^mi?b%l*$" ) then
                               n      = n * 1000
                               suffix = false
                           elseif suffix:match( "^ki?b%l*$" ) then
                               suffix = false
                           end
                       end
                   end
                   if suffix then
                       fehler( "Wert", "Byte-Einheit nicht erkannt" )
                   else
                       if n > 1000 then
                           n      = math.ceil( n * 0.01 ) * 0.1
                           size   = string.format( "%.1f", n )
                           suffix = "MB"
                           lot    = ( n > KBytesMax )
                       else
                           suffix = "kB"
                       end
                   end
                   sKBytes = facet( size:gsub( "%.", "," ), suffix )
                   if lot then
                       sKBytes = string.format( "%s", sKBytes )
                       if not large then
                           fehler( "Wert", "zu viele MegaBytes" )
                       end
                   end
               else
                   fehler( "Wert", "KiloByte-Zahl nicht erkannt" )
               end
               r = string.format( "%s%s%s", r, sep, sKBytes )
               if large and not lot then
                   fehler( "Wert", "Fehlplatziertes [sic!]" )
               end
               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 = " [%s]"
               end
               r = string.format( sep, r )
           end
           redundanz( some )
       elseif sFormat or sKBytes or sAbruf then
           local s = feed( "bas", "Titel" )
           if not s  or  not s:find( "//" ) then
               fehler( "Problem",
                       "Dateiformat/Größe/Abruf nur wenn Weblink" )
           end
       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
           sAutor = sAutor:gsub( "^#", "#" ) -- nick, hashtag
                          :gsub( "^%*", "*" )
           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 'Hrsg' 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 )
       Ortsname( sOrt, "ed1", "Ort" )
       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 )
               Ortsname( sOrt, "orig", "Ort" )
           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 )  and
              ( sJahr or not sOrt:match( "%.$" ) ) 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( "%s", 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( "%[", "[" )
                       :gsub( "%]", "]" )
               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
               Text = Zitation.fetch( "Text" )
               if stop == "" then
                   local strip = Resultat:gsub( "$", "" )
                   if not Text.sentenceTerminated( strip ) then
                       stop = "."
                   end
               elseif sTitelErg then
                   stop = ""
               end
               stop = stop .. " "
               s    = Herausgeber( sHrsg, false, true )
               if not ( sReihe  or
                        Text.sentenceTerminated( s ) ) then
                   s = s .. "."
               end
               fein( stop, s )
           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( "(= %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" )
       if sOrt  and
          ( feed( "id", "ISSN" )  or  feed( "id", "ZDB" ) ) then
           sOrt = false
       end
       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 then
       s = sOrt:gsub( "[,/; ]+$", "" )
       r = string.format( "%s%s%s", r, sep, s )
       if s ~= sOrt then
           Zitation.fill( "print", "Ort", s )
       end
       Ortsname( sOrt, "print", "Ort" )
       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 )   or
              ( o.zone  and  not o.hour ) then
               o = false
           elseif feed( "id", "ISBN" ) then
               s = tostring( o.year )
           elseif feed( "www", "URL" ) then
               s = o:format( "T._Monat JJJJ hh:mm:ss Zone" )
           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%s",
                              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,
                                      " ", ", " )
       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 S.", sUmfang )
       end
       r   = string.format( "%s%s%s", r, sep, sUmfang )
       sep = ", "
   end
   local sanitize = feed( "www", "Weblink" )
   if sanitize and sanitize:find( "InternetArchiveBot", 1, true ) then
       -- 2018-12-04
       mw.addWarning( "Unerlaubte Bot-Aktion
" .. "Informationsverlust vorheriger Angaben.
" .. "Unzulässige Formatierung.
" .. sanitize ) fein( "", "") Zitation.o.www = false

-- Wiederholt ZR-widrig den Titel der Website u. a. 171587445/183321268

   end
   if feed( "www" ) then
       local sWeblink = feed( "www", "Weblink" )
       if sWeblink then
           local WLink = Zitation.fetch( "WLink" )
           local show  = WLink.getWeblink( sWeblink )
           local spec  = resourceMeta( false )
           show = show:gsub( " www%d*%.", " " )
           r    = string.format( "%s%s%s%s",
                                 r, sep, show, spec )
           if spec == "" then
               sep = " – "
           else
               sep = " "
           end
           if sWeblink:find( "", 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
           local s = feed( "bas", "Titel" )
           if not s  or  not s:find( "//" ) then
               fehler( "Problem",
                       "Dateiformat/Größe/Abruf nur bei externem Link" )
           end
       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 )
       if not s:match( "%.$" ) then
           sep = "."
       end
   end
   s = klammerInhalt()
   if s then
       if Resultat:find( "%)$" ) then
           -- Irgendwas zuvor endet auf eine runde Klammer.
           sep = " – ("
       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.failure = function ( alert, always )

   -- Ausgabe von Fehlern mit class=error
   -- Parameter:
   --     alert   -- string, mit Fehlerliste, oder nil
   --     always  -- do not hide: boolean, or nil
   -- Rückgabewert: string, ggf. mit Fehlermeldung
   local r
   if alert then
       local TemplUtl = Zitation.fetch( "TemplUtl" )
       local light    = feed( "leise", "leiser" )  and  not always
       local self     = feed( "leise", "Vorlage" )  or  Selbst
       r = string.format( "Fehler in %s – %s",
                          self, alert )
       r = TemplUtl.failure( r,  not light, false, Zitation.frame )
   else
       r = ""
   end
   return r

end -- Zitation.failure()


Zitation.fault = function ( a, always )

   -- Formatiere Fehler als teils ausgeblendet
   -- Parameter:
   --     a       -- string, mit Fehlermeldung
   --     always  -- true, wenn nicht zu unterdrücken
   -- Rückgabewert:
   --     string, ggf. mit umschließendem HTML-Element
   local r
   if not always  and
      feed( "leise", "leiser" )  and
      mw.site.server == "//de.wikipedia.org" then
       if fading() then
           local e = mw.html.create( "span" )
           e:addClass( "Zitationsfehler Zitationswartung" )
            :css( "display", "none" )
            :wikitext( a )
           r = tostring( e )
       end
   end
   return r or a

end -- Zitation.fault()


Zitation.fetch = function ( assigned, acquire )

   -- Binde Modul ein
   -- Parameter:
   --     assigned  -- string mit Name
   --                  "arXiv"
   --                  "bibcode"
   --                  "DateTime"
   --                  "JSTOR"
   --                  "Multilingual"
   --                  "TemplUtl"
   --                  "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
       local lucky, g
       if acquire then
           s = acquire
       end
       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

if not v:find( "InternetArchiveBot", 1, true ) then

               fehler( "Wert",
                       string.format( "'%s' mit Wikisyntax", s ) )

end

           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
   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 
   -- 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.text.killMarkers( v )    -- <math>
           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( "%s",
                      s,
                      "style='display:none'",
                      " " )
   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", story, show )
           end
           show = string.format( "(%s)", show )
           plus:addClass( "ISBN-bad-code" )
               :css( "white-space", "nowrap" )
               :wikitext( show )
           r = string.format( "%s %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( "%s", 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