Modul:Citation/CS1: razlika između inačica

Izvor: Hrvatska internetska enciklopedija
Prijeđi na navigaciju Prijeđi na pretraživanje
mNema sažetka uređivanja
Nema sažetka uređivanja
 
Nije prikazano 18 međuinačica
Redak 1: Redak 1:
-- Modul:Citation/CS1
-- Hrvatska internetska enciklopedija – profesionalno očišćena i proširena verzija CS1 sustava
require('strict')
require('strict')


local p = {}
local p = {}


-- Učitavamo konfiguraciju (date_names, id_handlers, whitelist...)
local cfg        = require('Module:Citation/CS1/Configuration')
local cfg = require('Module:Citation/CS1/Configuration')
local util      = require('Module:Citation/CS1/Utilities')
local ids        = require('Module:Citation/CS1/Identifiers')
local whitelist = require('Module:Citation/CS1/Whitelist')
local dateval    = require('Module:Citation/CS1/Date_validation')


--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- STILSKA PRAVILA (FANDOM / ENWIKI CS1)
-- ISO 639-1 → puni nazivi jezika
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------


local function cleanPunctuation(text)
local iso639 = {
     if not text then return nil end
    en = "engleski",
     text = mw.text.trim(text)
    de = "njemacki",
     -- ukloni završne zareze, točke, dvotočke
    fr = "francuski",
    text = text:gsub('[%.,:;]+$', '')
    es = "spanjolski",
    return text
    it = "talijanski",
    hr = "hrvatski",
    sr = "srpski",
    bs = "bosanski",
    sl = "slovenski",
    ru = "ruski",
    pl = "poljski",
    cs = "ceski",
    da = "danski",
    sv = "svedski",
    fi = "finski",
    nl = "nizozemski",
}
 
--------------------------------------------------------------------------------
-- ISO 3166 → nazivi država
--------------------------------------------------------------------------------
 
local iso3166 = {
    DE = "Njemacka",
    HR = "Hrvatska",
    AT = "Austrija",
    CH = "Svicaraska",
    IT = "Italija",
    FR = "Francuska",
}
 
local function normalizeLanguage(lang)
     if not lang then return nil end
     local code = mw.ustring.lower(mw.text.trim(lang))
     return iso639[code] or lang
end
end


local function ensurePeriod(text)
local function normalizeCountry(code)
     if not text or text == '' then return nil end
     if not code then return nil end
     if text:match('[%.%!%?]$') then
     code = mw.ustring.upper(mw.text.trim(code))
        return text
     return iso3166[code] or code
    end
     return text .. '.'
end
end


local function cs1Italic(text)
--------------------------------------------------------------------------------
     if not text or text == '' then return nil end
-- ISBN VALIDACIJA
     return "''" .. cleanPunctuation(text) .. "''"
--------------------------------------------------------------------------------
end
 
local function validateISBN(isbn)
     if not isbn then return nil end
     isbn = isbn:gsub("[^0-9Xx]", "")


local function cs1Quoted(text)
    -- ISBN-10
    if not text or text == '' then return nil end
    if #isbn == 10 then
    return '„' .. cleanPunctuation(text) .. '“'
        local sum = 0
end
        for i = 1, 9 do
            sum = sum + tonumber(isbn:sub(i,i)) * (11 - i)
        end
        local check = isbn:sub(10,10)
        check = (check == "X" or check == "x") and 10 or tonumber(check)
        if (sum + check) % 11 == 0 then
            return isbn
        end
        return isbn .. " (neispravan ISBN-10)"
    end


local function cs1Join(parts)
    -- ISBN-13
     local out = {}
     if #isbn == 13 then
    for _, p in ipairs(parts) do
        local sum = 0
         if p and p ~= '' then
        for i = 1, 12 do
             table.insert(out, p)
            local n = tonumber(isbn:sub(i,i))
            sum = sum + (i % 2 == 0 and n * 3 or n)
        end
        local check = tonumber(isbn:sub(13,13))
         if (10 - (sum % 10)) % 10 == check then
             return isbn
         end
         end
        return isbn .. " (neispravan ISBN-13)"
     end
     end
    return table.concat(out, ' ')
end


local function cs1Sentence(parts)
     return isbn .. " (neispravan format)"
    local line = cs1Join(parts)
     return ensurePeriod(line)
end
end


--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- POMOĆNE FUNKCIJE
-- FORMATIRANJE UREDNIKA I PREVODITELJA
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------


-- Vrati prvi ne-prazan parametar iz liste mogućih imena
local function formatEditors(args)
local function getArg(args, names)
    local list = {}
     for _, name in ipairs(names) do
 
         local val = args[name]
    if util.is_set(args.editor) then
         if type(val) == 'string' then
        table.insert(list, args.editor)
             val = mw.text.trim(val)
    end
            if val ~= '' then
     for i = 1, 9 do
                return val, name
         local e = args["editor" .. i]
            end
         if util.is_set(e) then
             table.insert(list, e)
         end
         end
     end
     end
     return nil, nil
 
     if #list == 0 then return nil end
    if #list == 1 then return "Uredio: " .. list[1] end
    if #list == 2 then return "Uredili: " .. list[1] .. " i " .. list[2] end
 
    local last = table.remove(list)
    return "Uredili: " .. table.concat(list, ", ") .. " i " .. last
end
end


-- Omotaj tekst u wikitext kurziv
local function formatTranslators(args)
local function italic(text)
    local list = {}
     if not text or text == '' then
 
         return nil
     if util.is_set(args.translator) then
        table.insert(list, args.translator)
    end
    for i = 1, 9 do
        local t = args["translator" .. i]
        if util.is_set(t) then
            table.insert(list, t)
         end
     end
     end
     return "''" .. text .. "''"
 
     if #list == 0 then return nil end
    if #list == 1 then return "Preveo: " .. list[1] end
    if #list == 2 then return "Preveli: " .. list[1] .. " i " .. list[2] end
 
    local last = table.remove(list)
    return "Preveli: " .. table.concat(list, ", ") .. " i " .. last
end
end


-- Omotaj tekst u navodnike
--------------------------------------------------------------------------------
local function quote(text)
-- POMOĆNE FUNKCIJE
     if not text or text == '' then
--------------------------------------------------------------------------------
        return nil
 
     end
local function clean(s)
     -- hrvatski navodnici
     if not s then return nil end
     return '' .. text .. ''
     return mw.text.trim(s):gsub('[%.,;:]+$', '')
end
 
local function italic(s)
     if not util.is_set(s) then return nil end
     return "''" .. clean(s) .. "''"
end
end


-- Spoji dijelove s razmacima, ignorirajući nil/prazno
local function quoted(s)
local function joinWithSpace(parts)
     if not util.is_set(s) then return nil end
     local out = {}
     return '„' .. clean(s) .. ''
    for _, part in ipairs(parts) do
        if part and part ~= '' then
            table.insert(out, part)
        end
    end
     return table.concat(out, ' ')
end
end


-- Spoji dijelove s točkom i razmakom, brinući o završnoj točki
local function sentence(parts)
local function joinSentences(parts)
     local line = util.join(parts, ' ')
     local out = {}
    if not line or line == '' then return nil end
    for _, part in ipairs(parts) do
    if not line:match('[%.%!%?]$') then
        if part and part ~= '' then
        line = line .. '.'
            -- Dodaj točku ako ne završava s točkom ili upitnikom ili uskličnikom
            if not part:match('[%.%!%?]$') then
                part = part .. '.'
            end
            table.insert(out, part)
        end
     end
     end
     return table.concat(out, ' ')
     return line
end
end


-- Formatiraj godinu/datum
--------------------------------------------------------------------------------
local function formatDate(args)
-- NORMALIZACIJA KLASE (OMOGUĆENE SVE TVOJE KLASE)
    -- podrška za |date= i |year=
--------------------------------------------------------------------------------
    local date = args['date']
    local year = args['year']


     if date and mw.text.trim(date) ~= '' then
local function normalizeClass(args, frame)
         return mw.text.trim(date)
     local cls = args.CitationClass or args.type
    if cls then
         return mw.text.trim(cls):lower()
     end
     end


     if year and mw.text.trim(year) ~= '' then
    -- ako postoji URL, a klasa nije zadana → web
         return mw.text.trim(year)
     if util.is_set(args.url) then
         return 'web'
     end
     end


     return nil
     local title = frame:getTitle() or (frame:getParent() and frame:getParent():getTitle()) or ''
    title = mw.ustring.lower(title)
 
    if title:find('citiranje knjige', 1, true)        then return 'book' end
    if title:find('citiranje weba', 1, true)          then return 'web' end
    if title:find('citiranje casopisa', 1, true)      then return 'journal' end
    if title:find('citiranje novina', 1, true)        then return 'newspaper' end
    if title:find('citiranje enciklopedije', 1, true) then return 'encyclopedia' end
    if title:find('citiranje zakona', 1, true)        then return 'law' end
    if title:find('citiranje arhive', 1, true)        then return 'archive' end
    if title:find('citiranje intervjua', 1, true)    then return 'interview' end
 
    return 'web'
end
end
--------------------------------------------------------------------------------
-- HRVATSKI ALIAS PARAMETRI
--------------------------------------------------------------------------------
local hr_aliases = {
    autor      = "author",
    autor1    = "author1",
    autor2    = "author2",
    autor3    = "author3",


-- Formatiraj autore (samo osnovno: author / author1 / author2 / author3)
    naslov    = "title",
local function formatAuthors(args)
 
     local names = {}
    izdavac    = "publisher",
     mjesto    = "location",
    place      = "location",
 
    godina    = "year",


     local mainAuthor = getArg(args, {'author', 'last1', 'surname1'})
     stranica  = "pages",
     if mainAuthor then
     stranice  = "pages",
        table.insert(names, mainAuthor)
    end


     -- ostali autori
     urednik    = "editor",
    for i = 2, 9 do
    izdanje    = "edition",
        local a = args['author' .. i] or args['last' .. i] or args['surname' .. i]
        if a and mw.text.trim(a) ~= '' then
            table.insert(names, mw.text.trim(a))
        end
    end


     if #names == 0 then
     novine    = "newspaper",
        return nil
    enciklopedija = "encyclopedia",
     end
    zakon      = "law",
     arhiva    = "archive",
    intervju  = "interview",
}


    if #names == 1 then
--------------------------------------------------------------------------------
        return names[1]
-- getArgs() S MAPIRANJEM HR PARAMETARA
    elseif #names == 2 then
--------------------------------------------------------------------------------
        return names[1] .. ' i ' .. names[2]
    else
        -- npr. "Ivić, Horvat i drugi"
        local last = table.remove(names)
        return table.concat(names, ', ') .. ' i ' .. last
    end
end


-- Formatiraj identifikatore (ISBN, DOI, PMID...)
local function getArgs(frame)
local function formatIds(args)
     local args = require('Module:Arguments').getArgs(frame)
     local parts = {}


     for idName, handler in pairs(cfg.id_handlers or {}) do
     for hr, en in pairs(hr_aliases) do
         local lower = idName:lower()
         if args[hr] and not args[en] then
        local val = args[lower] or args[idName]
             args[en] = args[hr]
        if val and mw.text.trim(val) ~= '' then
             val = mw.text.trim(val)
            if handler.link then
                table.insert(parts, string.format('%s: [%s %s]', idName, handler.link:gsub('$1', val), val))
            else
                table.insert(parts, string.format('%s: %s', idName, val))
            end
         end
         end
     end
     end


     if #parts == 0 then
     if args.page and not args.pages then
         return nil
         args.pages = args.page
     end
     end


     return table.concat(parts, ' · ')
     return args
end
end


--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- CS1-LIKE VALIDATOR
-- VALIDACIJA PARAMETARA
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------


Redak 193: Redak 260:
     local errors = {}
     local errors = {}


     -- 1. Nepoznati parametri
     -- Nepoznati parametri
     for k, v in pairs(args) do
     for k, v in pairs(args) do
         if type(k) == 'string' then
         if type(k) == 'string' and not whitelist.params[k] then
            if not cfg.valid_params[k] then
            table.insert(errors, "Nepoznat parametar: <code>" .. k .. "</code>")
                table.insert(errors, "Nepoznat parametar: <code>" .. k .. "</code>")
            end
         end
         end
     end
     end


     -- 2. Obavezni parametri
     -- Obavezni parametri
     for _, req in ipairs(cfg.required_params[cls] or {}) do
     for _, req in ipairs(cfg.required_params[cls] or {}) do
         if not args[req] or mw.text.trim(args[req]) == '' then
         if not util.is_set(args[req]) then
             table.insert(errors, "Nedostaje obavezni parametar: <code>" .. req .. "</code>")
             table.insert(errors, "Nedostaje obavezni parametar: <code>" .. req .. "</code>")
         end
         end
     end
     end


     -- 3. Parametri koji ne pripadaju klasi
     -- Parametri koji nisu dopušteni u klasi
     local allowed = {}
     local allowed = {}
     for _, p in ipairs(cfg.class_params[cls] or {}) do
     for _, p in ipairs(cfg.class_params[cls] or {}) do
Redak 216: Redak 281:


     for k, v in pairs(args) do
     for k, v in pairs(args) do
         if type(k) == 'string' and cfg.valid_params[k] and not allowed[k] then
         if type(k) == 'string' and whitelist.params[k] and not allowed[k] then
             table.insert(errors, "Parametar <code>" .. k .. "</code> nije dopušten u klasi <code>" .. cls .. "</code>")
             table.insert(errors, "Parametar <code>" .. k .. "</code> nije dopušten u klasi <code>" .. cls .. "</code>")
         end
         end
     end
     end


     -- 4. Bare URL (CS1 stil)
     -- Bare URL
     if args.url and (not args.title or args.title == '') then
     if util.is_set(args.url) and not util.is_set(args.title) then
         table.insert(errors, "Bare URL: nedostaje parametar <code>title</code>")
         table.insert(errors, "Bare URL: nedostaje parametar <code>title</code>")
     end
     end


     -- 5. Prazni parametri
     -- Prazni parametri
     for k, v in pairs(args) do
     for k, v in pairs(args) do
         if type(k) == 'string' and type(v) == 'string' and mw.text.trim(v) == '' then
         if type(k) == 'string' and type(v) == 'string' and mw.text.trim(v) == '' then
Redak 236: Redak 301:
end
end


local function normalizeClass(args, frame)
--------------------------------------------------------------------------------
    local cls = args['CitationClass'] or args['type']
-- FORMATIRANJE IDENTIFIKATORA
--------------------------------------------------------------------------------
 
local function formatIds(args)
    local out = {}
    for id, _ in pairs(cfg.id_handlers or {}) do
        local val = args[id:lower()] or args[id]
        if util.is_set(val) then
            table.insert(out, ids.format(id, val))
        end
    end
    return util.join(out, ' · ')
end
 
--------------------------------------------------------------------------------
-- FORMATIRANJE AUTORA
--------------------------------------------------------------------------------
 
local function formatAuthors(args)
    local list = {}


     if cls then
     if util.is_set(args.author) then
         cls = mw.text.trim(cls):lower()
         table.insert(list, args.author)
     else
     end
        local title = frame:getParent() and frame:getParent():getTitle() or frame:getTitle() or ''
        title = mw.ustring.lower(title)


        if title:find('cite web', 1, true) then
    for i = 1, 9 do
            cls = 'web'
        local a = args["author" .. i]
         elseif title:find('cite book', 1, true) then
         if util.is_set(a) then
             cls = 'book'
             table.insert(list, a)
        elseif title:find('cite journal', 1, true) then
            cls = 'journal'
        else
            cls = 'web'
         end
         end
     end
     end


     if cls == 'book' or cls == 'journal' or cls == 'web' then
     if #list == 0 then return nil end
        return cls
    if #list == 1 then return list[1] end
    end
    if #list == 2 then return list[1] .. " i " .. list[2] end


     return 'web'
    local last = table.remove(list)
     return table.concat(list, ", ") .. " i " .. last
end
end


--------------------------------------------------------------------------------
-- CITATION: BOOK
--------------------------------------------------------------------------------
local function citeBook(args)
    local parts = {}


local function validateParams(args, cls)
    local lang_full = normalizeLanguage(args.language)
     local errors = {}
    local isbn      = args.isbn and validateISBN(args.isbn) or nil
     local country  = normalizeCountry(args.country)
 
    table.insert(parts, sentence({
        formatAuthors(args),
        args.year and "(" .. args.year .. ")" or nil,
        italic(args.title),
        args.subtitle and italic(args.subtitle) or nil,
        lang_full and ("[jezik: " .. lang_full .. "]") or nil
    }))
 
    table.insert(parts, sentence({
        formatEditors(args),
        formatTranslators(args),
        args.edition and (args.edition .. ". izd.") or nil,
        args.series and ("Serija: " .. args.series) or nil,
        args.volume and ("Svezak " .. args.volume) or nil,
        util.join({args.location, args.publisher}, ": "),
        country and ("(" .. country .. ")") or nil,
        formatIds(args)
    }))


     for k, v in pairs(args) do
     if util.is_set(args.url) then
         if type(k) == 'string' then
         local url = "[" .. args.url .. " " .. args.url .. "]"
            if not cfg.valid_params[k] then
        if util.is_set(args["access-date"]) then
                table.insert(errors, "Nepoznat parametar: <code>" .. k .. "</code>")
            url = url .. " (pristupljeno " .. args["access-date"] .. ")"
            end
         end
         end
        table.insert(parts, sentence({url}))
    end
    if util.is_set(args.quote) then
        table.insert(parts, sentence({quoted(args.quote)}))
     end
     end


     for _, req in ipairs(cfg.required_params[cls] or {}) do
     return util.join(parts, " ")
        if not args[req] or mw.text.trim(args[req]) == '' then
end
            table.insert(errors, "Nedostaje obavezni parametar: <code>" .. req .. "</code>")
 
         end
--------------------------------------------------------------------------------
-- HR FORMAT DATUMA (jednostavna verzija za YYYY-MM-DD)
--------------------------------------------------------------------------------
 
local function formatDateHR(datestr)
    if not util.is_set(datestr) then
        return nil
    end
 
    local y, m, d = datestr:match('^(%d%d%d%d)%-(%d%d)%-(%d%d)$')
    if not (y and m and d) then
        -- ako nije ISO format, vrati original (već očišćen)
         return clean(datestr)
     end
     end


     local allowed = {}
    -- mapiranje mjeseca na hrvatski naziv (koristi cfg.date_names ako želiš)
     for _, p in ipairs(cfg.class_params[cls] or {}) do
     local month_names = {
         allowed[p] = true
        ['01'] = 'siječnja',
        ['02'] = 'veljače',
        ['03'] = 'ožujka',
        ['04'] = 'travnja',
        ['05'] = 'svibnja',
        ['06'] = 'lipnja',
        ['07'] = 'srpnja',
        ['08'] = 'kolovoza',
        ['09'] = 'rujna',
        ['10'] = 'listopada',
        ['11'] = 'studenoga',
        ['12'] = 'prosinca',
    }
 
     local month = month_names[m] or m
    return string.format('%s. %s %s.', tonumber(d), month, y)
end
 
 
--------------------------------------------------------------------------------
-- CITATION: WEB
--------------------------------------------------------------------------------
 
 
local function citeWeb(args)
    local parts = {}
 
    -- 1. Naslov (kao link, ali prikazuje se samo naslov)
    if util.is_set(args.url) and util.is_set(args.title) then
         table.insert(parts, sentence({
            "[" .. args.url .. " " .. clean(args.title) .. "]"
        }))
    elseif util.is_set(args.title) then
        table.insert(parts, sentence({
            clean(args.title)
        }))
     end
     end


     for k, v in pairs(args) do
     -- 2. Website / izdavač
        if type(k) == 'string' and cfg.valid_params[k] and not allowed[k] then
    local site = args.website or args.work or args.publisher
            table.insert(errors, "Parametar <code>" .. k .. "</code> nije dopušten u klasi <code>" .. cls .. "</code>")
    if util.is_set(site) then
         end
        table.insert(parts, sentence({
            clean(site)
         }))
     end
     end


     if args.url and (not args.title or args.title == '') then
    -- 3. Datum objave (hr format ako je moguće)
         table.insert(errors, "Bare URL: nedostaje parametar <code>title</code>")
     if util.is_set(args.date) then
        table.insert(parts, sentence({
            formatDateHR(args.date)
        }))
    elseif util.is_set(args.year) then
         table.insert(parts, sentence({
            clean(args.year)
        }))
     end
     end


     for k, v in pairs(args) do
     -- 4. Pristupljeno
        if type(k) == 'string' and type(v) == 'string' and mw.text.trim(v) == '' then
    if util.is_set(args["access-date"]) then
            table.insert(errors, "Prazan parametar: <code>" .. k .. "</code>")
        table.insert(parts, sentence({
         end
            "Pristupljeno " .. clean(args["access-date"])
         }))
     end
     end


     return errors
     return util.join(parts, " ")
end
end




--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- GLAVNA FUNKCIJA
-- CITATION: JOURNAL
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------


function p.citation(frame)
local function citeJournal(args)
     local parent = frame:getParent()
     local parts = {}
    local args = parent and parent.args or frame.args
    local lang_full = normalizeLanguage(args.language)
 
    table.insert(parts, sentence({
        formatAuthors(args),
        args.year and "(" .. args.year .. ")" or nil,
        quoted(args.title),
        lang_full and ("[jezik: " .. lang_full .. "]") or nil
    }))
 
    table.insert(parts, sentence({
        italic(args.journal),
        util.join({args.volume, args.issue and "(" .. args.issue .. ")" or nil}, " "),
        args.pages and ("str. " .. args.pages) or nil,
        formatIds(args)
    }))


     local cls = normalizeClass(args, frame)
     if util.is_set(args.url) then
    local errors = validateParams(args, cls)
        local url = "[" .. args.url .. " " .. args.url .. "]"
        if util.is_set(args["access-date"]) then
            url = url .. " (pristupljeno " .. args["access-date"] .. ")"
        end
        table.insert(parts, sentence({url}))
    end


     if #errors > 0 then
     if util.is_set(args.quote) then
      local out = '<div class="citation-errors" style="color:#b00; font-size:90%;">'
        table.insert(parts, sentence({quoted(args.quote)}))
      for _, e in ipairs(errors) do
          out = out .. '• ' .. e .. '<br>'
      end
      out = out .. '</div>'
      return out
     end
     end


    return util.join(parts, " ")
end


    -- Uzimamo standardne parametre
--------------------------------------------------------------------------------
    local authors = formatAuthors(args)
-- CITATION: NEWSPAPER
    local title = getArg(args, {'title'})
--------------------------------------------------------------------------------
    local work = getArg(args, {'work', 'journal', 'website'})
    local publisher = getArg(args, {'publisher'})
    local date = formatDate(args)
    local url = getArg(args, {'url'})
    local accessDate = getArg(args, {'access-date', 'accessdate'})
    local language = getArg(args, {'language', 'lang'})
    local quoteText = getArg(args, {'quote'})
    local idText = formatIds(args)


     -- Dijelovi citata
local function citeNewspaper(args)
     local parts = {}
    local lang_full = normalizeLanguage(args.language)


     local firstPart = {}
     table.insert(parts, sentence({
        formatAuthors(args),
        args.date and formatDateHR(args.date) or args.year,
        quoted(args.title),
        lang_full and ("[jezik: " .. lang_full .. "]") or nil
    }))


     if authors then
     table.insert(parts, sentence({
         table.insert(firstPart, authors)
        italic(args.newspaper or args.work),
        util.join({args.location, args.publisher}, ": "),
        args.pages and ("str. " .. args.pages) or nil,
        formatIds(args)
    }))
 
    if util.is_set(args.url) then
        local url = "[" .. args.url .. " " .. args.url .. "]"
        if util.is_set(args["access-date"]) then
            url = url .. " (pristupljeno " .. args["access-date"] .. ")"
        end
         table.insert(parts, sentence({url}))
     end
     end


     if date then
     if util.is_set(args.quote) then
        -- godina/datum u zagradama
         table.insert(parts, sentence({quoted(args.quote)}))
         table.insert(firstPart, '(' .. date .. ')')
     end
     end


     if title then
     return util.join(parts, " ")
        table.insert(firstPart, italic(title))
end
     end
 
--------------------------------------------------------------------------------
-- CITATION: ENCYCLOPEDIA
--------------------------------------------------------------------------------
 
local function citeEncyclopedia(args)
     local parts = {}
    local lang_full = normalizeLanguage(args.language)


     local mainLine = joinWithSpace(firstPart)
     table.insert(parts, sentence({
        formatAuthors(args),
        italic(args.title),
        lang_full and ("[jezik: " .. lang_full .. "]") or nil
    }))


     local secondParts = {}
     table.insert(parts, sentence({
        italic(args.encyclopedia or args.work),
        args.volume and ("Svezak " .. args.volume) or nil,
        args.edition and (args.edition .. ". izd.") or nil,
        util.join({args.location, args.publisher}, ": "),
        args.year,
        args.pages and ("str. " .. args.pages) or nil,
        formatIds(args)
    }))


     if work then
     if util.is_set(args.url) then
         table.insert(secondParts, italic(work))
        local url = "[" .. args.url .. " " .. args.url .. "]"
        if util.is_set(args["access-date"]) then
            url = url .. " (pristupljeno " .. args["access-date"] .. ")"
        end
         table.insert(parts, sentence({url}))
     end
     end


     if publisher then
     if util.is_set(args.quote) then
         table.insert(secondParts, publisher)
         table.insert(parts, sentence({quoted(args.quote)}))
     end
     end


     if language then
     return util.join(parts, " ")
         table.insert(secondParts, 'Na jeziku: ' .. language)
end
 
--------------------------------------------------------------------------------
-- CITATION: LAW
--------------------------------------------------------------------------------
 
local function citeLaw(args)
    local parts = {}
    local lang_full = normalizeLanguage(args.language)
 
    table.insert(parts, sentence({
        italic(args.title or args.law),
        args.jurisdiction,
        args.year or args.date,
         lang_full and ("[jezik: " .. lang_full .. "]") or nil
    }))
 
    table.insert(parts, sentence({
        args.article and ("cl. " .. args.article) or nil,
        args.section and ("§ " .. args.section) or nil,
        args.chapter and ("pog. " .. args.chapter) or nil,
        args.publisher
    }))
 
    if util.is_set(args.url) then
        local url = "[" .. args.url .. " " .. args.url .. "]"
        if util.is_set(args["access-date"]) then
            url = url .. " (pristupljeno " .. args["access-date"] .. ")"
        end
        table.insert(parts, sentence({url}))
     end
     end


     if idText then
     if util.is_set(args.notes) then
         table.insert(secondParts, idText)
         table.insert(parts, sentence({args.notes}))
     end
     end


     local secondLine = joinWithSpace(secondParts)
     return util.join(parts, " ")
end
 
--------------------------------------------------------------------------------
-- CITATION: ARCHIVE
--------------------------------------------------------------------------------


     local urlPart = nil
local function citeArchive(args)
     if url then
     local parts = {}
         if accessDate then
    local lang_full = normalizeLanguage(args.language)
            urlPart = string.format('[%s %s] (pristupljeno %s)', url, url, accessDate)
 
         else
     table.insert(parts, sentence({
            urlPart = string.format('[%s %s]', url, url)
        formatAuthors(args),
         italic(args.title),
        args.year or args.date,
        lang_full and ("[jezik: " .. lang_full .. "]") or nil
    }))
 
    table.insert(parts, sentence({
        args.archive,
        args.collection,
        args.identifier,
        util.join({args.location}, ", "),
         args.pages and ("str. " .. args.pages) or nil
    }))
 
    if util.is_set(args.url) then
        local url = "[" .. args.url .. " " .. args.url .. "]"
        if util.is_set(args["access-date"]) then
            url = url .. " (pristupljeno " .. args["access-date"] .. ")"
         end
         end
        table.insert(parts, sentence({url}))
     end
     end


    local quotePart = nil
     if util.is_set(args.quote) then
     if quoteText then
         table.insert(parts, sentence({quoted(args.quote)}))
         quotePart = quote(quoteText)
     end
     end


     -- Sastavi sve "rečenice"
     return util.join(parts, " ")
    local sentences = {}
end
 
--------------------------------------------------------------------------------
-- CITATION: INTERVIEW
--------------------------------------------------------------------------------


     if mainLine ~= '' then
local function citeInterview(args)
         table.insert(sentences, mainLine)
    local parts = {}
    local lang_full = normalizeLanguage(args.language)
 
    table.insert(parts, sentence({
        args.interviewer and ("Intervju: " .. args.interviewer) or nil,
        formatAuthors(args),
        quoted(args.title),
        lang_full and ("[jezik: " .. lang_full .. "]") or nil
    }))
 
    table.insert(parts, sentence({
        italic(args.program or args.work),
        args.publisher,
        args.location,
        args.date or args.year
    }))
 
     if util.is_set(args.url) then
        local url = "[" .. args.url .. " " .. args.url .. "]"
        if util.is_set(args["access-date"]) then
            url = url .. " (pristupljeno " .. args["access-date"] .. ")"
        end
         table.insert(parts, sentence({url}))
     end
     end


     if secondLine ~= '' then
     if util.is_set(args.quote) then
         table.insert(sentences, secondLine)
         table.insert(parts, sentence({quoted(args.quote)}))
     end
     end


     if urlPart then
    return util.join(parts, " ")
         table.insert(sentences, urlPart)
end
 
--------------------------------------------------------------------------------
-- GLAVNA FUNKCIJA
--------------------------------------------------------------------------------
 
function p.citation(frame)
    local args = getArgs(frame)
    local cls  = normalizeClass(args, frame)
 
    -- Validacija
    local errors = validateParams(args, cls)
     if #errors > 0 then
         local out = '<div class="citation-errors" style="color:#b00; font-size:90%;">'
        for _, e in ipairs(errors) do
            out = out .. '• ' .. e .. '<br>'
        end
        out = out .. '</div>'
        return out
     end
     end


     if quotePart then
    -- Dispatch prema klasi
         table.insert(sentences, quotePart)
     if cls == 'book' then
        return citeBook(args)
    elseif cls == 'journal' then
        return citeJournal(args)
    elseif cls == 'newspaper' then
         return citeNewspaper(args)
    elseif cls == 'encyclopedia' then
        return citeEncyclopedia(args)
    elseif cls == 'law' then
        return citeLaw(args)
    elseif cls == 'archive' then
        return citeArchive(args)
    elseif cls == 'interview' then
        return citeInterview(args)
    else
        return citeWeb(args)
     end
     end
end
--------------------------------------------------------------------------------
-- ALIAS ZA #invoke: ... |main
--------------------------------------------------------------------------------
p.main = p.citation
--------------------------------------------------------------------------------
-- BIBLIOGRAFIJA
--------------------------------------------------------------------------------


     -- Ako baš ništa nema, vrati poruku
function p.bibliography(frame)
     if #sentences == 0 then
     local args = frame.args
         return '<span class="error">Greška u citatu: nedostaju osnovni parametri (npr. title, url).</span>'
     local out  = {}
 
    local index = 1
    while args[tostring(index)] do
         table.insert(out, "* " .. args[tostring(index)])
        index = index + 1
     end
     end


     return joinSentences(sentences)
     return table.concat(out, "\n")
end
end


--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- EXPORT
-- POVRAT MODULA
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------


return {
return p
    citation = p.citation,
    main    = p.citation
}

Posljednja izmjena od 22. siječanj 2026. u 12:18

Dokumentacija modula
Ovaj modul i njegovi pod-moduli podržavaju CS1 i CS2 stil citiranja. Ovaj modul nije namijenjen za direktno pozivanje, već ga je potrebno pozvati iz predloška koji ga koristi.

<section begin=module_components_table /> Pod-moduli navedeni niže osiguravaju funkcionalnost predložaka za for CS1/2 stil citiranja:

CS1 / CS2 moduli
Pod-modul Opis
Gold padlock Modul:Citation/CS1 Odgovoran za prikazivanje i druge podržane funkcije
Modul:Citation/CS1/Configuration Tablice prijevoda; Rukovanje pogreškama i identifikatorima
Modul:Citation/CS1/Whitelist Popis aktivnih i zastarjelih parametara za citiranje
Modul:Citation/CS1/Date validation Rukovanje oblicima datuma i validacija njihove točnosti
Modul:Citation/CS1/Identifiers Funkcije za rukovanje imenovanim identifikatorima (ISBN, DOI, PMID, i sl.)
Modul:Citation/CS1/Utilities Često korištene tablice i funkcije
Modul:Citation/CS1/COinS Funkcije koje stvaraju meta-podatke o citiranom CS1/2 izvoru
Modul:Citation/CS1/styles.css CSS stilovi primijenjeni na predloške za citiranje
Silver padlock Modul:Citation/CS1/Suggestions Popias koji mapira česte netočno navedene parametre i dodjeljuje im ispravna imena
<section end=module_components_table />


-- Modul:Citation/CS1
-- Hrvatska internetska enciklopedija – profesionalno očišćena i proširena verzija CS1 sustava

require('strict')

local p = {}

local cfg        = require('Module:Citation/CS1/Configuration')
local util       = require('Module:Citation/CS1/Utilities')
local ids        = require('Module:Citation/CS1/Identifiers')
local whitelist  = require('Module:Citation/CS1/Whitelist')
local dateval    = require('Module:Citation/CS1/Date_validation')

--------------------------------------------------------------------------------
-- ISO 639-1 → puni nazivi jezika
--------------------------------------------------------------------------------

local iso639 = {
    en = "engleski",
    de = "njemacki",
    fr = "francuski",
    es = "spanjolski",
    it = "talijanski",
    hr = "hrvatski",
    sr = "srpski",
    bs = "bosanski",
    sl = "slovenski",
    ru = "ruski",
    pl = "poljski",
    cs = "ceski",
    da = "danski",
    sv = "svedski",
    fi = "finski",
    nl = "nizozemski",
}

--------------------------------------------------------------------------------
-- ISO 3166 → nazivi država
--------------------------------------------------------------------------------

local iso3166 = {
    DE = "Njemacka",
    HR = "Hrvatska",
    AT = "Austrija",
    CH = "Svicaraska",
    IT = "Italija",
    FR = "Francuska",
}

local function normalizeLanguage(lang)
    if not lang then return nil end
    local code = mw.ustring.lower(mw.text.trim(lang))
    return iso639[code] or lang
end

local function normalizeCountry(code)
    if not code then return nil end
    code = mw.ustring.upper(mw.text.trim(code))
    return iso3166[code] or code
end

--------------------------------------------------------------------------------
-- ISBN VALIDACIJA
--------------------------------------------------------------------------------

local function validateISBN(isbn)
    if not isbn then return nil end
    isbn = isbn:gsub("[^0-9Xx]", "")

    -- ISBN-10
    if #isbn == 10 then
        local sum = 0
        for i = 1, 9 do
            sum = sum + tonumber(isbn:sub(i,i)) * (11 - i)
        end
        local check = isbn:sub(10,10)
        check = (check == "X" or check == "x") and 10 or tonumber(check)
        if (sum + check) % 11 == 0 then
            return isbn
        end
        return isbn .. " (neispravan ISBN-10)"
    end

    -- ISBN-13
    if #isbn == 13 then
        local sum = 0
        for i = 1, 12 do
            local n = tonumber(isbn:sub(i,i))
            sum = sum + (i % 2 == 0 and n * 3 or n)
        end
        local check = tonumber(isbn:sub(13,13))
        if (10 - (sum % 10)) % 10 == check then
            return isbn
        end
        return isbn .. " (neispravan ISBN-13)"
    end

    return isbn .. " (neispravan format)"
end

--------------------------------------------------------------------------------
-- FORMATIRANJE UREDNIKA I PREVODITELJA
--------------------------------------------------------------------------------

local function formatEditors(args)
    local list = {}

    if util.is_set(args.editor) then
        table.insert(list, args.editor)
    end
    for i = 1, 9 do
        local e = args["editor" .. i]
        if util.is_set(e) then
            table.insert(list, e)
        end
    end

    if #list == 0 then return nil end
    if #list == 1 then return "Uredio: " .. list[1] end
    if #list == 2 then return "Uredili: " .. list[1] .. " i " .. list[2] end

    local last = table.remove(list)
    return "Uredili: " .. table.concat(list, ", ") .. " i " .. last
end

local function formatTranslators(args)
    local list = {}

    if util.is_set(args.translator) then
        table.insert(list, args.translator)
    end
    for i = 1, 9 do
        local t = args["translator" .. i]
        if util.is_set(t) then
            table.insert(list, t)
        end
    end

    if #list == 0 then return nil end
    if #list == 1 then return "Preveo: " .. list[1] end
    if #list == 2 then return "Preveli: " .. list[1] .. " i " .. list[2] end

    local last = table.remove(list)
    return "Preveli: " .. table.concat(list, ", ") .. " i " .. last
end

--------------------------------------------------------------------------------
-- POMOĆNE FUNKCIJE
--------------------------------------------------------------------------------

local function clean(s)
    if not s then return nil end
    return mw.text.trim(s):gsub('[%.,;:]+$', '')
end

local function italic(s)
    if not util.is_set(s) then return nil end
    return "''" .. clean(s) .. "''"
end

local function quoted(s)
    if not util.is_set(s) then return nil end
    return '„' .. clean(s) .. '“'
end

local function sentence(parts)
    local line = util.join(parts, ' ')
    if not line or line == '' then return nil end
    if not line:match('[%.%!%?]$') then
        line = line .. '.'
    end
    return line
end

--------------------------------------------------------------------------------
-- NORMALIZACIJA KLASE (OMOGUĆENE SVE TVOJE KLASE)
--------------------------------------------------------------------------------

local function normalizeClass(args, frame)
    local cls = args.CitationClass or args.type
    if cls then
        return mw.text.trim(cls):lower()
    end

    -- ako postoji URL, a klasa nije zadana → web
    if util.is_set(args.url) then
        return 'web'
    end

    local title = frame:getTitle() or (frame:getParent() and frame:getParent():getTitle()) or ''
    title = mw.ustring.lower(title)

    if title:find('citiranje knjige', 1, true)        then return 'book' end
    if title:find('citiranje weba', 1, true)          then return 'web' end
    if title:find('citiranje casopisa', 1, true)      then return 'journal' end
    if title:find('citiranje novina', 1, true)        then return 'newspaper' end
    if title:find('citiranje enciklopedije', 1, true) then return 'encyclopedia' end
    if title:find('citiranje zakona', 1, true)        then return 'law' end
    if title:find('citiranje arhive', 1, true)        then return 'archive' end
    if title:find('citiranje intervjua', 1, true)     then return 'interview' end

    return 'web'
end
--------------------------------------------------------------------------------
-- HRVATSKI ALIAS PARAMETRI
--------------------------------------------------------------------------------

local hr_aliases = {
    autor      = "author",
    autor1     = "author1",
    autor2     = "author2",
    autor3     = "author3",

    naslov     = "title",

    izdavac    = "publisher",
    mjesto     = "location",
    place      = "location",

    godina     = "year",

    stranica   = "pages",
    stranice   = "pages",

    urednik    = "editor",
    izdanje    = "edition",

    novine     = "newspaper",
    enciklopedija = "encyclopedia",
    zakon      = "law",
    arhiva     = "archive",
    intervju   = "interview",
}

--------------------------------------------------------------------------------
-- getArgs() S MAPIRANJEM HR PARAMETARA
--------------------------------------------------------------------------------

local function getArgs(frame)
    local args = require('Module:Arguments').getArgs(frame)

    for hr, en in pairs(hr_aliases) do
        if args[hr] and not args[en] then
            args[en] = args[hr]
        end
    end

    if args.page and not args.pages then
        args.pages = args.page
    end

    return args
end

--------------------------------------------------------------------------------
-- VALIDACIJA PARAMETARA
--------------------------------------------------------------------------------

local function validateParams(args, cls)
    local errors = {}

    -- Nepoznati parametri
    for k, v in pairs(args) do
        if type(k) == 'string' and not whitelist.params[k] then
            table.insert(errors, "Nepoznat parametar: <code>" .. k .. "</code>")
        end
    end

    -- Obavezni parametri
    for _, req in ipairs(cfg.required_params[cls] or {}) do
        if not util.is_set(args[req]) then
            table.insert(errors, "Nedostaje obavezni parametar: <code>" .. req .. "</code>")
        end
    end

    -- Parametri koji nisu dopušteni u klasi
    local allowed = {}
    for _, p in ipairs(cfg.class_params[cls] or {}) do
        allowed[p] = true
    end

    for k, v in pairs(args) do
        if type(k) == 'string' and whitelist.params[k] and not allowed[k] then
            table.insert(errors, "Parametar <code>" .. k .. "</code> nije dopušten u klasi <code>" .. cls .. "</code>")
        end
    end

    -- Bare URL
    if util.is_set(args.url) and not util.is_set(args.title) then
        table.insert(errors, "Bare URL: nedostaje parametar <code>title</code>")
    end

    -- Prazni parametri
    for k, v in pairs(args) do
        if type(k) == 'string' and type(v) == 'string' and mw.text.trim(v) == '' then
            table.insert(errors, "Prazan parametar: <code>" .. k .. "</code>")
        end
    end

    return errors
end

--------------------------------------------------------------------------------
-- FORMATIRANJE IDENTIFIKATORA
--------------------------------------------------------------------------------

local function formatIds(args)
    local out = {}
    for id, _ in pairs(cfg.id_handlers or {}) do
        local val = args[id:lower()] or args[id]
        if util.is_set(val) then
            table.insert(out, ids.format(id, val))
        end
    end
    return util.join(out, ' · ')
end

--------------------------------------------------------------------------------
-- FORMATIRANJE AUTORA
--------------------------------------------------------------------------------

local function formatAuthors(args)
    local list = {}

    if util.is_set(args.author) then
        table.insert(list, args.author)
    end

    for i = 1, 9 do
        local a = args["author" .. i]
        if util.is_set(a) then
            table.insert(list, a)
        end
    end

    if #list == 0 then return nil end
    if #list == 1 then return list[1] end
    if #list == 2 then return list[1] .. " i " .. list[2] end

    local last = table.remove(list)
    return table.concat(list, ", ") .. " i " .. last
end

--------------------------------------------------------------------------------
-- CITATION: BOOK
--------------------------------------------------------------------------------

local function citeBook(args)
    local parts = {}

    local lang_full = normalizeLanguage(args.language)
    local isbn      = args.isbn and validateISBN(args.isbn) or nil
    local country   = normalizeCountry(args.country)

    table.insert(parts, sentence({
        formatAuthors(args),
        args.year and "(" .. args.year .. ")" or nil,
        italic(args.title),
        args.subtitle and italic(args.subtitle) or nil,
        lang_full and ("[jezik: " .. lang_full .. "]") or nil
    }))

    table.insert(parts, sentence({
        formatEditors(args),
        formatTranslators(args),
        args.edition and (args.edition .. ". izd.") or nil,
        args.series and ("Serija: " .. args.series) or nil,
        args.volume and ("Svezak " .. args.volume) or nil,
        util.join({args.location, args.publisher}, ": "),
        country and ("(" .. country .. ")") or nil,
        formatIds(args)
    }))

    if util.is_set(args.url) then
        local url = "[" .. args.url .. " " .. args.url .. "]"
        if util.is_set(args["access-date"]) then
            url = url .. " (pristupljeno " .. args["access-date"] .. ")"
        end
        table.insert(parts, sentence({url}))
    end

    if util.is_set(args.quote) then
        table.insert(parts, sentence({quoted(args.quote)}))
    end

    return util.join(parts, " ")
end

--------------------------------------------------------------------------------
-- HR FORMAT DATUMA (jednostavna verzija za YYYY-MM-DD)
--------------------------------------------------------------------------------

local function formatDateHR(datestr)
    if not util.is_set(datestr) then
        return nil
    end

    local y, m, d = datestr:match('^(%d%d%d%d)%-(%d%d)%-(%d%d)$')
    if not (y and m and d) then
        -- ako nije ISO format, vrati original (već očišćen)
        return clean(datestr)
    end

    -- mapiranje mjeseca na hrvatski naziv (koristi cfg.date_names ako želiš)
    local month_names = {
        ['01'] = 'siječnja',
        ['02'] = 'veljače',
        ['03'] = 'ožujka',
        ['04'] = 'travnja',
        ['05'] = 'svibnja',
        ['06'] = 'lipnja',
        ['07'] = 'srpnja',
        ['08'] = 'kolovoza',
        ['09'] = 'rujna',
        ['10'] = 'listopada',
        ['11'] = 'studenoga',
        ['12'] = 'prosinca',
    }

    local month = month_names[m] or m
    return string.format('%s. %s %s.', tonumber(d), month, y)
end


--------------------------------------------------------------------------------
-- CITATION: WEB
--------------------------------------------------------------------------------


local function citeWeb(args)
    local parts = {}

    -- 1. Naslov (kao link, ali prikazuje se samo naslov)
    if util.is_set(args.url) and util.is_set(args.title) then
        table.insert(parts, sentence({
            "[" .. args.url .. " " .. clean(args.title) .. "]"
        }))
    elseif util.is_set(args.title) then
        table.insert(parts, sentence({
            clean(args.title)
        }))
    end

    -- 2. Website / izdavač
    local site = args.website or args.work or args.publisher
    if util.is_set(site) then
        table.insert(parts, sentence({
            clean(site)
        }))
    end

    -- 3. Datum objave (hr format ako je moguće)
    if util.is_set(args.date) then
        table.insert(parts, sentence({
            formatDateHR(args.date)
        }))
    elseif util.is_set(args.year) then
        table.insert(parts, sentence({
            clean(args.year)
        }))
    end

    -- 4. Pristupljeno
    if util.is_set(args["access-date"]) then
        table.insert(parts, sentence({
            "Pristupljeno " .. clean(args["access-date"])
        }))
    end

    return util.join(parts, " ")
end


--------------------------------------------------------------------------------
-- CITATION: JOURNAL
--------------------------------------------------------------------------------

local function citeJournal(args)
    local parts = {}
    local lang_full = normalizeLanguage(args.language)

    table.insert(parts, sentence({
        formatAuthors(args),
        args.year and "(" .. args.year .. ")" or nil,
        quoted(args.title),
        lang_full and ("[jezik: " .. lang_full .. "]") or nil
    }))

    table.insert(parts, sentence({
        italic(args.journal),
        util.join({args.volume, args.issue and "(" .. args.issue .. ")" or nil}, " "),
        args.pages and ("str. " .. args.pages) or nil,
        formatIds(args)
    }))

    if util.is_set(args.url) then
        local url = "[" .. args.url .. " " .. args.url .. "]"
        if util.is_set(args["access-date"]) then
            url = url .. " (pristupljeno " .. args["access-date"] .. ")"
        end
        table.insert(parts, sentence({url}))
    end

    if util.is_set(args.quote) then
        table.insert(parts, sentence({quoted(args.quote)}))
    end

    return util.join(parts, " ")
end

--------------------------------------------------------------------------------
-- CITATION: NEWSPAPER
--------------------------------------------------------------------------------

local function citeNewspaper(args)
    local parts = {}
    local lang_full = normalizeLanguage(args.language)

    table.insert(parts, sentence({
        formatAuthors(args),
        args.date and formatDateHR(args.date) or args.year,
        quoted(args.title),
        lang_full and ("[jezik: " .. lang_full .. "]") or nil
    }))

    table.insert(parts, sentence({
        italic(args.newspaper or args.work),
        util.join({args.location, args.publisher}, ": "),
        args.pages and ("str. " .. args.pages) or nil,
        formatIds(args)
    }))

    if util.is_set(args.url) then
        local url = "[" .. args.url .. " " .. args.url .. "]"
        if util.is_set(args["access-date"]) then
            url = url .. " (pristupljeno " .. args["access-date"] .. ")"
        end
        table.insert(parts, sentence({url}))
    end

    if util.is_set(args.quote) then
        table.insert(parts, sentence({quoted(args.quote)}))
    end

    return util.join(parts, " ")
end

--------------------------------------------------------------------------------
-- CITATION: ENCYCLOPEDIA
--------------------------------------------------------------------------------

local function citeEncyclopedia(args)
    local parts = {}
    local lang_full = normalizeLanguage(args.language)

    table.insert(parts, sentence({
        formatAuthors(args),
        italic(args.title),
        lang_full and ("[jezik: " .. lang_full .. "]") or nil
    }))

    table.insert(parts, sentence({
        italic(args.encyclopedia or args.work),
        args.volume and ("Svezak " .. args.volume) or nil,
        args.edition and (args.edition .. ". izd.") or nil,
        util.join({args.location, args.publisher}, ": "),
        args.year,
        args.pages and ("str. " .. args.pages) or nil,
        formatIds(args)
    }))

    if util.is_set(args.url) then
        local url = "[" .. args.url .. " " .. args.url .. "]"
        if util.is_set(args["access-date"]) then
            url = url .. " (pristupljeno " .. args["access-date"] .. ")"
        end
        table.insert(parts, sentence({url}))
    end

    if util.is_set(args.quote) then
        table.insert(parts, sentence({quoted(args.quote)}))
    end

    return util.join(parts, " ")
end

--------------------------------------------------------------------------------
-- CITATION: LAW
--------------------------------------------------------------------------------

local function citeLaw(args)
    local parts = {}
    local lang_full = normalizeLanguage(args.language)

    table.insert(parts, sentence({
        italic(args.title or args.law),
        args.jurisdiction,
        args.year or args.date,
        lang_full and ("[jezik: " .. lang_full .. "]") or nil
    }))

    table.insert(parts, sentence({
        args.article and ("cl. " .. args.article) or nil,
        args.section and ("§ " .. args.section) or nil,
        args.chapter and ("pog. " .. args.chapter) or nil,
        args.publisher
    }))

    if util.is_set(args.url) then
        local url = "[" .. args.url .. " " .. args.url .. "]"
        if util.is_set(args["access-date"]) then
            url = url .. " (pristupljeno " .. args["access-date"] .. ")"
        end
        table.insert(parts, sentence({url}))
    end

    if util.is_set(args.notes) then
        table.insert(parts, sentence({args.notes}))
    end

    return util.join(parts, " ")
end

--------------------------------------------------------------------------------
-- CITATION: ARCHIVE
--------------------------------------------------------------------------------

local function citeArchive(args)
    local parts = {}
    local lang_full = normalizeLanguage(args.language)

    table.insert(parts, sentence({
        formatAuthors(args),
        italic(args.title),
        args.year or args.date,
        lang_full and ("[jezik: " .. lang_full .. "]") or nil
    }))

    table.insert(parts, sentence({
        args.archive,
        args.collection,
        args.identifier,
        util.join({args.location}, ", "),
        args.pages and ("str. " .. args.pages) or nil
    }))

    if util.is_set(args.url) then
        local url = "[" .. args.url .. " " .. args.url .. "]"
        if util.is_set(args["access-date"]) then
            url = url .. " (pristupljeno " .. args["access-date"] .. ")"
        end
        table.insert(parts, sentence({url}))
    end

    if util.is_set(args.quote) then
        table.insert(parts, sentence({quoted(args.quote)}))
    end

    return util.join(parts, " ")
end

--------------------------------------------------------------------------------
-- CITATION: INTERVIEW
--------------------------------------------------------------------------------

local function citeInterview(args)
    local parts = {}
    local lang_full = normalizeLanguage(args.language)

    table.insert(parts, sentence({
        args.interviewer and ("Intervju: " .. args.interviewer) or nil,
        formatAuthors(args),
        quoted(args.title),
        lang_full and ("[jezik: " .. lang_full .. "]") or nil
    }))

    table.insert(parts, sentence({
        italic(args.program or args.work),
        args.publisher,
        args.location,
        args.date or args.year
    }))

    if util.is_set(args.url) then
        local url = "[" .. args.url .. " " .. args.url .. "]"
        if util.is_set(args["access-date"]) then
            url = url .. " (pristupljeno " .. args["access-date"] .. ")"
        end
        table.insert(parts, sentence({url}))
    end

    if util.is_set(args.quote) then
        table.insert(parts, sentence({quoted(args.quote)}))
    end

    return util.join(parts, " ")
end

--------------------------------------------------------------------------------
-- GLAVNA FUNKCIJA
--------------------------------------------------------------------------------

function p.citation(frame)
    local args = getArgs(frame)
    local cls  = normalizeClass(args, frame)

    -- Validacija
    local errors = validateParams(args, cls)
    if #errors > 0 then
        local out = '<div class="citation-errors" style="color:#b00; font-size:90%;">'
        for _, e in ipairs(errors) do
            out = out .. '• ' .. e .. '<br>'
        end
        out = out .. '</div>'
        return out
    end

    -- Dispatch prema klasi
    if cls == 'book' then
        return citeBook(args)
    elseif cls == 'journal' then
        return citeJournal(args)
    elseif cls == 'newspaper' then
        return citeNewspaper(args)
    elseif cls == 'encyclopedia' then
        return citeEncyclopedia(args)
    elseif cls == 'law' then
        return citeLaw(args)
    elseif cls == 'archive' then
        return citeArchive(args)
    elseif cls == 'interview' then
        return citeInterview(args)
    else
        return citeWeb(args)
    end
end

--------------------------------------------------------------------------------
-- ALIAS ZA #invoke: ... |main
--------------------------------------------------------------------------------

p.main = p.citation

--------------------------------------------------------------------------------
-- BIBLIOGRAFIJA
--------------------------------------------------------------------------------

function p.bibliography(frame)
    local args = frame.args
    local out  = {}

    local index = 1
    while args[tostring(index)] do
        table.insert(out, "* " .. args[tostring(index)])
        index = index + 1
    end

    return table.concat(out, "\n")
end

--------------------------------------------------------------------------------
-- POVRAT MODULA
--------------------------------------------------------------------------------

return p