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
Redak 1: Redak 1:
-- Modul:Citation/CS1
-- Hrvatska, stabilna i pojednostavljena 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')
-- STILSKA PRAVILA (FANDOM / ENWIKI CS1)
local whitelist = require('Module:Citation/CS1/Whitelist')
--------------------------------------------------------------------------------
local dateval = require('Module:Citation/CS1/Date_validation')
 
local function cleanPunctuation(text)
    if not text then return nil end
    text = mw.text.trim(text)
    -- ukloni završne zareze, točke, dvotočke
    text = text:gsub('[%.,:;]+$', '')
    return text
end
 
local function ensurePeriod(text)
    if not text or text == '' then return nil end
    if text:match('[%.%!%?]$') then
        return text
    end
    return text .. '.'
end
 
local function cs1Italic(text)
    if not text or text == '' then return nil end
    return "''" .. cleanPunctuation(text) .. "''"
end
 
local function cs1Quoted(text)
    if not text or text == '' then return nil end
    return '„' .. cleanPunctuation(text) .. '“'
end
 
local function cs1Join(parts)
    local out = {}
    for _, p in ipairs(parts) do
        if p and p ~= '' then
            table.insert(out, p)
        end
    end
    return table.concat(out, ' ')
end
 
local function cs1Sentence(parts)
    local line = cs1Join(parts)
    return ensurePeriod(line)
end
 


--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
Redak 56: Redak 16:
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------


-- Vrati prvi ne-prazan parametar iz liste mogućih imena
local function getArgs(frame)
local function getArg(args, names)
     return require('Module:Arguments').getArgs(frame)
     for _, name in ipairs(names) do
        local val = args[name]
        if type(val) == 'string' then
            val = mw.text.trim(val)
            if val ~= '' then
                return val, name
            end
        end
    end
    return nil, nil
end
end


-- Omotaj tekst u wikitext kurziv
local function clean(s)
local function italic(text)
     if not s then return nil end
     if not text or text == '' then
     return mw.text.trim(s):gsub('[%.,;:]+$', '')
        return nil
    end
     return "''" .. text .. "''"
end
end


-- Omotaj tekst u navodnike
local function italic(s)
local function quote(text)
     if not util.is_set(s) then return nil end
     if not text or text == '' then
     return "''" .. clean(s) .. "''"
        return nil
    end
    -- hrvatski navodnici
     return '' .. text .. ''
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 normalizeClass(args, frame)
local function formatDate(args)
     local cls = args.CitationClass or args.type
    -- podrška za |date= i |year=
     if cls then
     local date = args['date']
         cls = mw.text.trim(cls):lower()
    local year = args['year']
 
     if date and mw.text.trim(date) ~= '' then
         return mw.text.trim(date)
    end
 
    if year and mw.text.trim(year) ~= '' then
        return mw.text.trim(year)
    end
 
    return nil
end
 
-- Formatiraj autore (samo osnovno: author / author1 / author2 / author3)
local function formatAuthors(args)
    local names = {}
 
    local mainAuthor = getArg(args, {'author', 'last1', 'surname1'})
    if mainAuthor then
        table.insert(names, mainAuthor)
    end
 
    -- ostali autori
    for i = 2, 9 do
        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
        return nil
    end
 
    if #names == 1 then
        return names[1]
    elseif #names == 2 then
        return names[1] .. ' i ' .. names[2]
     else
     else
        -- npr. "Ivić, Horvat i drugi"
         local title = frame:getParent() and frame:getParent():getTitle() or frame:getTitle()
         local last = table.remove(names)
         title = mw.ustring.lower(title or '')
        return table.concat(names, ', ') .. ' i ' .. last
        if title:find('cite web', 1, true) then cls = 'web'
    end
        elseif title:find('cite book', 1, true) then cls = 'book'
end
        elseif title:find('cite journal', 1, true) then cls = 'journal'
 
        else cls = 'web'
-- Formatiraj identifikatore (ISBN, DOI, PMID...)
local function formatIds(args)
    local parts = {}
 
    for idName, handler in pairs(cfg.id_handlers or {}) do
        local lower = idName:lower()
         local val = args[lower] or args[idName]
        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 cls ~= 'web' and cls ~= 'book' and cls ~= 'journal' then
     if #parts == 0 then
         cls = 'web'
         return nil
     end
     end
 
     return cls
     return table.concat(parts, ' · ')
end
end


--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- CS1-LIKE VALIDATOR
-- VALIDACIJA PARAMETARA (CS1-like)
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------


Redak 193: Redak 70:
     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 po klasi
     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 ne pripadaju 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 91:


     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 111:
end
end


local function normalizeClass(args, frame)
--------------------------------------------------------------------------------
    local cls = args['CitationClass'] or args['type']
-- FORMATIRANJE IDENTIFIKATORA
--------------------------------------------------------------------------------


    if cls then
local function formatIds(args)
        cls = mw.text.trim(cls):lower()
     local out = {}
     else
    for id, _ in pairs(cfg.id_handlers or {}) do
        local title = frame:getParent() and frame:getParent():getTitle() or frame:getTitle() or ''
         local val = args[id:lower()] or args[id]
         title = mw.ustring.lower(title)
         if util.is_set(val) then
 
             table.insert(out, ids.format(id, val))
         if title:find('cite web', 1, true) then
             cls = 'web'
        elseif title:find('cite book', 1, true) then
            cls = 'book'
        elseif title:find('cite journal', 1, true) then
            cls = 'journal'
        else
            cls = 'web'
         end
         end
     end
     end
 
     return util.join(out, ' · ')
    if cls == 'book' or cls == 'journal' or cls == 'web' then
        return cls
    end
 
     return 'web'
end
end


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


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


     for k, v in pairs(args) do
     if util.is_set(args.author) then
         if type(k) == 'string' then
         table.insert(list, args.author)
            if not cfg.valid_params[k] then
                table.insert(errors, "Nepoznat parametar: <code>" .. k .. "</code>")
            end
        end
     end
     end


     for _, req in ipairs(cfg.required_params[cls] or {}) do
     for i = 1, 9 do
         if not args[req] or mw.text.trim(args[req]) == '' then
         local a = args['author' .. i]
             table.insert(errors, "Nedostaje obavezni parametar: <code>" .. req .. "</code>")
        if util.is_set(a) then
             table.insert(list, a)
         end
         end
     end
     end


     local allowed = {}
     if #list == 0 then return nil end
     for _, p in ipairs(cfg.class_params[cls] or {}) do
     if #list == 1 then return list[1] end
        allowed[p] = true
     if #list == 2 then return list[1] .. ' i ' .. list[2] end
    end
 
     for k, v in pairs(args) do
        if type(k) == 'string' and cfg.valid_params[k] and not allowed[k] then
            table.insert(errors, "Parametar <code>" .. k .. "</code> nije dopušten u klasi <code>" .. cls .. "</code>")
        end
    end


     if args.url and (not args.title or args.title == '') then
     local last = table.remove(list)
        table.insert(errors, "Bare URL: nedostaje parametar <code>title</code>")
    return table.concat(list, ', ') .. ' i ' .. last
    end
 
    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
end


--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- GLAVNA FUNKCIJA
-- CITATION: WEB
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------


function p.citation(frame)
local function citeWeb(args)
     local parent = frame:getParent()
     local parts = {}
     local args = parent and parent.args or frame.args
 
    table.insert(parts, sentence({
        formatAuthors(args),
        args.date and '(' .. args.date .. ')' or nil,
        italic(args.title)
     }))
 
    table.insert(parts, sentence({
        italic(args.work),
        args.publisher,
        args.language and ('Na jeziku: ' .. args.language) 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: BOOK
    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 citeBook(args)
     local parts = {}


     local firstPart = {}
     table.insert(parts, sentence({
        formatAuthors(args),
        args.year and '(' .. args.year .. ')' or nil,
        italic(args.title)
    }))


     if authors then
     table.insert(parts, sentence({
         table.insert(firstPart, authors)
        args.editor and ('Uredio: ' .. args.editor) or nil,
     end
        args.edition and (args.edition .. ' izd.') or nil,
        util.join({args.location, args.publisher}, ': '),
         args.language and ('Na jeziku: ' .. args.language) or nil,
        formatIds(args)
     }))


     if date then
     if util.is_set(args.url) then
         -- godina/datum u zagradama
         local url = '[' .. args.url .. ' ' .. args.url .. ']'
         table.insert(firstPart, '(' .. date .. ')')
         if util.is_set(args['access-date']) then
            url = url .. ' (pristupljeno ' .. args['access-date'] .. ')'
        end
        table.insert(parts, sentence({url}))
     end
     end


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


     local mainLine = joinWithSpace(firstPart)
     return util.join(parts, ' ')
end


    local secondParts = {}
--------------------------------------------------------------------------------
-- CITATION: JOURNAL
--------------------------------------------------------------------------------


    if work then
local function citeJournal(args)
        table.insert(secondParts, italic(work))
     local parts = {}
     end


     if publisher then
     table.insert(parts, sentence({
        table.insert(secondParts, publisher)
        formatAuthors(args),
     end
        args.year and '(' .. args.year .. ')' or nil,
        quoted(args.title)
     }))


     if language then
     table.insert(parts, sentence({
         table.insert(secondParts, 'Na jeziku: ' .. language)
        italic(args.journal),
    end
        util.join({args.volume, args.issue and '(' .. args.issue .. ')' or nil}, ' '),
 
         args.pages and ('str. ' .. args.pages) or nil,
    if idText then
        args.language and ('Na jeziku: ' .. args.language) or nil,
         table.insert(secondParts, idText)
         formatIds(args)
     end
     }))


     local secondLine = joinWithSpace(secondParts)
     if util.is_set(args.url) then
 
        local url = '[' .. args.url .. ' ' .. args.url .. ']'
    local urlPart = nil
         if util.is_set(args['access-date']) then
    if url then
             url = url .. ' (pristupljeno ' .. args['access-date'] .. ')'
         if accessDate then
            urlPart = string.format('[%s %s] (pristupljeno %s)', url, url, accessDate)
        else
             urlPart = string.format('[%s %s]', url, url)
         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


    if mainLine ~= '' then
--------------------------------------------------------------------------------
        table.insert(sentences, mainLine)
-- GLAVNA FUNKCIJA
    end
--------------------------------------------------------------------------------


     if secondLine ~= '' then
function p.citation(frame)
        table.insert(sentences, secondLine)
     local args = getArgs(frame)
    end
    local cls = normalizeClass(args, frame)


     if urlPart then
    -- Validacija
         table.insert(sentences, urlPart)
    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
     if cls == 'book' then
         table.insert(sentences, quotePart)
         return citeBook(args)
     end
     elseif cls == 'journal' then
 
        return citeJournal(args)
     -- Ako baš ništa nema, vrati poruku
     else
    if #sentences == 0 then
         return citeWeb(args)
         return '<span class="error">Greška u citatu: nedostaju osnovni parametri (npr. title, url).</span>'
     end
     end
    return joinSentences(sentences)
end
end


--------------------------------------------------------------------------------
-- Alias za #invoke: ... |main
-- EXPORT
p.main = p.citation
--------------------------------------------------------------------------------


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

Inačica od 12. siječanj 2026. u 09:03

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, stabilna i pojednostavljena 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')

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

local function getArgs(frame)
    return require('Module:Arguments').getArgs(frame)
end

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

local function normalizeClass(args, frame)
    local cls = args.CitationClass or args.type
    if cls then
        cls = mw.text.trim(cls):lower()
    else
        local title = frame:getParent() and frame:getParent():getTitle() or frame:getTitle()
        title = mw.ustring.lower(title or '')
        if title:find('cite web', 1, true) then cls = 'web'
        elseif title:find('cite book', 1, true) then cls = 'book'
        elseif title:find('cite journal', 1, true) then cls = 'journal'
        else cls = 'web'
        end
    end
    if cls ~= 'web' and cls ~= 'book' and cls ~= 'journal' then
        cls = 'web'
    end
    return cls
end

--------------------------------------------------------------------------------
-- VALIDACIJA PARAMETARA (CS1-like)
--------------------------------------------------------------------------------

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 po klasi
    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 ne pripadaju 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: WEB
--------------------------------------------------------------------------------

local function citeWeb(args)
    local parts = {}

    table.insert(parts, sentence({
        formatAuthors(args),
        args.date and '(' .. args.date .. ')' or nil,
        italic(args.title)
    }))

    table.insert(parts, sentence({
        italic(args.work),
        args.publisher,
        args.language and ('Na jeziku: ' .. args.language) 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: BOOK
--------------------------------------------------------------------------------

local function citeBook(args)
    local parts = {}

    table.insert(parts, sentence({
        formatAuthors(args),
        args.year and '(' .. args.year .. ')' or nil,
        italic(args.title)
    }))

    table.insert(parts, sentence({
        args.editor and ('Uredio: ' .. args.editor) or nil,
        args.edition and (args.edition .. ' izd.') or nil,
        util.join({args.location, args.publisher}, ': '),
        args.language and ('Na jeziku: ' .. args.language) 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: JOURNAL
--------------------------------------------------------------------------------

local function citeJournal(args)
    local parts = {}

    table.insert(parts, sentence({
        formatAuthors(args),
        args.year and '(' .. args.year .. ')' or nil,
        quoted(args.title)
    }))

    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,
        args.language and ('Na jeziku: ' .. args.language) 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

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

    if cls == 'book' then
        return citeBook(args)
    elseif cls == 'journal' then
        return citeJournal(args)
    else
        return citeWeb(args)
    end
end

-- Alias za #invoke: ... |main
p.main = p.citation

return p