Modul:Popis epizoda

Izvor: Hrvatska internetska enciklopedija
Inačica 771413 od 13. svibanj 2026. u 17:48 koju je unio Suradnik10 (razgovor | doprinosi) (preuzeto s hr.wikipedije)
(razl) ←Starija inačica | vidi trenutačnu inačicu (razl) | Novija inačica→ (razl)
Prijeđi na navigaciju Prijeđi na pretraživanje
Dokumentacija modula

Modul Popis epizoda omogućava funkcioniranje predloška {{popis epizoda}}. Modul stvara tablicu s podesivom bojom, širinom i tekstom zaglavlja te redove s informacijama o pojedinim epizodama.

Upotreba

{{#invoke:Popis epizoda|popis}}

Kategorije za praćenje


local p = {}
local dateModule = require('Module:Formatiraj datum')

function p.popis(frame)
    local args = frame:getParent().args
    local output = {}
    
    -- Prikazuje "Nadolazeća epizoda"
    local function nadolazeca()
        return '<span class="skin-nightmode-reset-color tv-tba" style="background:#eceff2; padding:1px 5px; border-radius:7px; display:inline-block; font-size:88%"><abbr title="Nadolazeća epizoda">N/E</abbr></span>'
    end

    -- Prikazuje "Nema podataka"
    local function nemapodataka()
        return '<span class="skin-nightmode-reset-color tv-tba" style="background:#eceff2; padding:1px 5px; border-radius:7px; display:inline-block; font-size:88%"><abbr title="Nema podataka">n.p.</abbr></span>'
    end
    
    -- Dohvaća boju zaglavlja i linija
    local rawColor = args["boja"] or args["pozadina"]
    local defaultColor = (rawColor and mw.text.trim(rawColor) ~= "") and rawColor or "#CCF"
    local textColor = frame:expandTemplate{
        title = "Jači kontrast boja",
        args  = { defaultColor, "white", "black" }
    }

    -- Dohvaća širinu tablice
    local rawWidth = args["širina"] 
    local tableWidth = "100%"
    if rawWidth and rawWidth ~= "" then
        if mw.ustring.match(rawWidth, "%%$") then
            tableWidth = rawWidth
        else
            tableWidth = rawWidth .. "%" 
        end
    end

    -- Postavlja zastavice za parametre
    local flags = {
        useBroj = false, useBrojsve = false, bothUsed = false, hasDatumHR = false, hasDatum = false, 
        hasNaslov = false, hasProducent = false, hasRedatelj = false, hasScenarist = false, hasAuxA = false, 
        hasAuxB = false, hasAuxC = false, hasAuxD = false, hasAuxE = false, hasGledanost = false,
        hasDatumALT = false, hasGosti = false, hasTrajanje = false, hasProdkod = false
    }
    
    -- Ključevi za provjeru
    local dioAnchors = {
        "brojsve", "broj", "naslov", "naslovHR", "datum", "datumHR", "datumALT", "scenarist", 
        "redatelj", "producent", "gledanost", "auxA", "auxB", "auxC", "auxD", "auxE", "gosti", "trajanje", "prodkod"
    }
    
    -- Ključevi za reference u zaglavlju
    local headerRefKeys = {
        naslov = "izvor naslov", naslovHR = "izvor naslovHR", datum = "izvor datum", 
        datumALT = "izvor datumALT", datumHR = "izvor datumHR", scenarist = "izvor scenarist", 
        redatelj = "izvor redatelj", producent = "izvor producent", prodkod = "izvor prodkod", gledanost = "izvor gledanost"
    }
    
    local lastBrojsve, lastBroj
    local concat = table.concat

    -- Traži maksimalan broj dijelova
    local function getMaxDio(i, keyPrefix)
        local max = 0 
        for d = 1, 100 do 
            if args[keyPrefix .. i .. "dio" .. d] and args[keyPrefix .. i .. "dio" .. d] ~= "" then
                max = d
            end 
        end
        return max > 0 and max or 1
    end
    
    -- Dohvaća vrijednost dijela
    local function getDioValue(i, key, d)
        local val = args[key .. i .. "dio" .. d]
        return (val and val ~= "") and val or nil
    end
    
    -- Dohvaća naslov po dijelovima
    local function getDioNaslovVariants(i, d)
        local results = {}
        local key = "naslov" .. i .. "dio" .. d
        local refKey = "Rnaslov" .. i .. "dio" .. d
        local val = args[key]
        local ref = args[refKey] or ""
        if val and val ~= "" then
            if string.match(val, "%s/%s") then
                local parts = mw.text.split(val, "%s/%s")
                for _, part in ipairs(parts) do
                    table.insert(results, { text = part, ref = "" })
                end
                if ref ~= "" and #results > 0 then
                    results[#results].ref = ref 
                end
            else
                table.insert(results, { text = val, ref = ref })
            end
        elseif ref ~= "" then
            table.insert(results, { text = "", ref = ref })
        end 
        return results 
    end

    -- Računa broj redova za spajanje
    local function getRowSpan(i, keyPrefix, maxDio, startDio)
        for d = startDio + 1, maxDio do
            local val = args[keyPrefix .. i .. "dio" .. d]
            local hasAny = false
            for _, anchor in ipairs(dioAnchors) do
                local otherVal = args[anchor .. i .. "dio" .. d]
                if otherVal and otherVal ~= "" then
                    hasAny = true 
                    break 
                end 
            end
            
            if val and val ~= "" then
                return d - startDio
            elseif not hasAny then
                return d - startDio
            end 
        end 
        return maxDio - startDio + 1 
    end

    -- Vraća maksimalan broj dijelova
    local function getMaxDioAcrossKeys(i, keys)
        local max = 1
        for _, key in ipairs(keys) do
            local count = getMaxDio(i, key)
            if count > max then max = count end
        end
        return max
    end

    -- Provjerava postoji li naslov
    local function hasNaslov(i)
        if args["naslov" .. i] or args["Rnaslov" .. i] then return true end
        for d = 1, 100 do
            local key = "naslov" .. i .. "dio" .. d
            local refKey = "Rnaslov" .. i .. "dio" .. d
            if (args[key] and args[key] ~= "") or (args[refKey] and args[refKey] ~= "") then
                return true
            end 
        end 
        return false 
    end

    -- Provjerava postoji li ALT naslov
    local function hasAlt(i)
        return args["naslovALT" .. i] or args["RnaslovALT" .. i] 
    end

    -- Dohvaća varijante naslova
    local function getTitleVariants(prefix, i)
        local results = {}
        local key = prefix .. i
        local val = args[key]
        local ref = args["R" .. prefix .. i] or ""
        if val and val ~= "" then
            if string.match(val, "%s/%s") then
                local parts = mw.text.split(val, "%s/%s")
                for _, part in ipairs(parts) do
                    table.insert(results, { text = part, ref = ref })
                end 
            else
                table.insert(results, { text = val, ref = ref }) 
            end
        elseif ref ~= "" then
            table.insert(results, { text = "", ref = ref })
        end 
        return results 
    end

    -- Dohvaća vrijednost polja
    local function getValue(i, key)
        if key == "redatelj" then return args["redatelj" .. i] or args["režija" .. i] or "" end
        if key == "scenarist" then return args["scenarist" .. i] or args["scenarij" .. i] or "" end
        if key == "gosti" then return args["gosti" .. i] or "" end
        if key == "trajanje" then return args["trajanje" .. i] or "" end 
        if key == "prodkod" then return args["prodkod" .. i] or "" end
        return args[key .. i] or ""
    end

    -- Provjerava ima li sadržaja
    local function hasAnyContent(i)
        for _, key in ipairs(dioAnchors) do
            if args[key .. i] and args[key .. i] ~= "" then
                return true
            end
        end
        
        for _, key in ipairs(dioAnchors) do
            for d = 1, 100 do
                if args[key .. i .. "dio" .. d] and args[key .. i .. "dio" .. d] ~= "" then
                    return true
                end
            end
        end
        
        if args["Rnaslov" .. i] and args["Rnaslov" .. i] ~= "" then
            return true
        end
        
        if args["kratkisadržaj" .. i] or args["sadržaj" .. i] then
            return true
        end
        
        return false
    end

    -- Gradi naslov epizode
    local function buildNaslov(i)
        local useQuotes = not (args["navodnici"] == "ne")

        local function splitAndQuote(text)
            local parts = {}
            local i_pos = 1
            while i_pos <= #text do
                local hrStart, hrEnd = mw.ustring.find(text, "<hr>", i_pos, true)
                local brStart, brEnd = mw.ustring.find(text, "<br>", i_pos, true)
                local slashStart, slashEnd = mw.ustring.find(text, " / ", i_pos)
                local nextBreak, breakType, breakEnd
                if hrStart then nextBreak = hrStart; breakType = "<hr>"; breakEnd = hrEnd end
                if brStart and (not nextBreak or brStart < nextBreak) then nextBreak = brStart; breakType = "<br>"; breakEnd = brEnd end
                if slashStart and (not nextBreak or slashStart < nextBreak) then nextBreak = slashStart; breakType = " / "; breakEnd = slashEnd end
                if nextBreak then
                    local segment = mw.text.trim(mw.ustring.sub(text, i_pos, nextBreak - 1))
                    table.insert(parts, useQuotes and ('"' .. segment .. '"') or segment)
                    table.insert(parts, breakType)
                    i_pos = breakEnd + 1
                else
                    local segment = mw.text.trim(mw.ustring.sub(text, i_pos))
                    table.insert(parts, useQuotes and ('"' .. segment .. '"') or segment)
                    break
                end
            end
            return table.concat(parts)
        end
        
        local function formatVariants(variants, separator)
            if #variants == 0 then return "" end
            local parts = {}
            for index, item in ipairs(variants) do
                local text = item.text
                local ref  = item.ref
                local quoted = splitAndQuote(text)
                if index == #variants and ref and ref ~= "" then
                    quoted = quoted .. ref
                end
                table.insert(parts, quoted)
            end 
            return separator .. table.concat(parts, " / ") 
        end

        local dio1Variants = getDioNaslovVariants(i, 1)
        if #dio1Variants > 0 then
            local allDioVariants = {}
            for d = 1, 100 do
                local dioVariants = getDioNaslovVariants(i, d)
                for _, v in ipairs(dioVariants) do
                    table.insert(allDioVariants, v)
                end 
            end 
            return formatVariants(allDioVariants, "") 
        end

        local mainVariants = getTitleVariants("naslov", i)
        if #mainVariants == 1 then
            local valUpper = mw.ustring.upper(mainVariants[1].text or "")
            if valUpper == "NE" or valUpper == "N/E" then
                return nadolazeca()
            end
        end

        local altVariants = getTitleVariants("naslovALT", i)
        local alt
        if #altVariants == 1 then
            local valUpper = mw.ustring.upper(altVariants[1].text or "")
            if valUpper == "NE" or valUpper == "N/E" then
                alt = '<br>' .. nadolazeca()
            else
                alt = formatVariants(altVariants, "<br>")
            end
        else
            alt = formatVariants(altVariants, "<br>")
        end

        local main = formatVariants(mainVariants, "")
        return main .. alt
    end

    -- Gradi hrvatski naslov
    local function buildNaslovHR(i)
        local useQuotes = not (args["navodnici"] == "ne")
        local naslov = args["naslovHR" .. i]
        local valUpper = mw.ustring.upper(naslov or "")
        if valUpper == "NE" or valUpper == "N/E" then
            return nadolazeca()
        end

        local ref = args["RnaslovHR" .. i] or ""
        if naslov and naslov ~= "" then
            local normalized = mw.ustring.gsub(naslov, "<hr>", "§§<hr>§§")
            normalized = mw.ustring.gsub(normalized, "<br>", "§§<br>§§")
            normalized = mw.ustring.gsub(normalized, " / ", "§§")
            local parts = mw.text.split(normalized, "§§")
            local quoted = {}
            for _, part in ipairs(parts) do
                local trimmed = mw.text.trim(part)
                if trimmed == "<hr>" or trimmed == "<br>" then
                    table.insert(quoted, trimmed)
                else
                    table.insert(quoted, useQuotes and ('"' .. trimmed .. '"') or trimmed)
                end
            end
            local result = ""
            for i_idx, part in ipairs(quoted) do
                if i_idx > 1 and part ~= "<hr>" and part ~= "<br>" and quoted[i_idx - 1] ~= "<hr>" and quoted[i_idx - 1] ~= "<br>" then
                    result = result .. " / "
                end
                result = result .. part
            end
            if ref ~= "" then result = result .. ref end
            return result
        elseif ref ~= "" then
            return ref
        end
        return ""
    end

    -- Prikupljanje indeksa epizoda
    local episodeIndices = {}
    for k, v in pairs(args) do
        local match = string.match(k, "^%a+(%d+)$")
        if match and v ~= "" then
            local index = tonumber(match)
            if index then
                episodeIndices[index] = true
            end
        end
        
        local dioMatch = string.match(k, "^%a+(%d+)dio%d+$")
        if dioMatch and v ~= "" then
            local index = tonumber(dioMatch)
            if index then
                episodeIndices[index] = true
            end
        end
    end

    -- Sortiranje epizoda
    local sortedIndices = {}
    for index in pairs(episodeIndices) do 
        table.insert(sortedIndices, index) 
    end 
    table.sort(sortedIndices)

    -- Glavna petlja za epizode
    for _, i in ipairs(sortedIndices) do
        -- Automatski broj epizode u seriji
        if args["brojsve" .. i] then
            lastBrojsve = args["brojsve" .. i]
        elseif lastBrojsve then
            local num = tonumber(string.match(lastBrojsve, "%d+"))
            if num then
                args["brojsve" .. i] = tostring(num + 1) .. "."
                lastBrojsve = args["brojsve" .. i]
            end
        end
        
        -- Check for dio parameters in brojsve
        for d = 1, 100 do
            if args["brojsve" .. i .. "dio" .. d] and args["brojsve" .. i .. "dio" .. d] ~= "" then
                lastBrojsve = args["brojsve" .. i .. "dio" .. d]
            end
        end

        -- Automatski broj epizode u sezoni
        if args["broj" .. i] then
            lastBroj = args["broj" .. i]
        elseif lastBroj then
            local num = tonumber(string.match(lastBroj, "%d+"))
            if num then
                args["broj" .. i] = tostring(num + 1) .. "."
                lastBroj = args["broj" .. i]
            end
        end
        
        -- Provjeri dio za posljednji broj
        for d = 1, 100 do
            if args["broj" .. i .. "dio" .. d] and args["broj" .. i .. "dio" .. d] ~= "" then
                lastBroj = args["broj" .. i .. "dio" .. d]
            end
        end

        -- Postavljanje zastavica za stupce
        if ((args["broj" .. i] and args["broj" .. i] ~= "") or (args["broj" .. i .. "dio1"] and args["broj" .. i .. "dio1"] ~= "")) and ((args["brojsve" .. i] and args["brojsve" .. i] ~= "") or (args["brojsve" .. i .. "dio1"] and args["brojsve" .. i .. "dio1"] ~= "")) then flags.bothUsed = true end
        if (args["broj" .. i] and args["broj" .. i] ~= "") or (args["broj" .. i .. "dio1"] and args["broj" .. i .. "dio1"] ~= "") then flags.useBroj = true end
        if (args["brojsve" .. i] and args["brojsve" .. i] ~= "") or (args["brojsve" .. i .. "dio1"] and args["brojsve" .. i .. "dio1"] ~= "") then flags.useBrojsve = true end
        if (args["gosti" .. i] and args["gosti" .. i] ~= "") or (args["gosti" .. i .. "dio1"] and args["gosti" .. i .. "dio1"] ~= "") then flags.hasGosti = true end
        if (args["trajanje" .. i] and args["trajanje" .. i] ~= "") or (args["trajanje" .. i .. "dio1"] and args["trajanje" .. i .. "dio1"] ~= "") then flags.hasTrajanje = true end
        if (args["datum" .. i] and args["datum" .. i] ~= "") or (args["datum" .. i .. "dio1"] and args["datum" .. i .. "dio1"] ~= "") then flags.hasDatum = true end
        if (args["datumHR" .. i] and args["datumHR" .. i] ~= "") or (args["datumHR" .. i .. "dio1"] and args["datumHR" .. i .. "dio1"] ~= "") then flags.hasDatumHR = true end
        if (args["datumALT" .. i] and args["datumALT" .. i] ~= "") or (args["datumALT" .. i .. "dio1"] and args["datumALT" .. i .. "dio1"] ~= "") then flags.hasDatumALT = true end
        if (args["prodkod" .. i] and args["prodkod" .. i] ~= "") or (args["prodkod" .. i .. "dio1"] and args["prodkod" .. i .. "dio1"] ~= "") then flags.hasProdkod = true end
        if hasNaslov(i) or hasAlt(i) or (args["naslov" .. i .. "dio1"] and args["naslov" .. i .. "dio1"] ~= "") or args["Rnaslov" .. i] then flags.hasNaslov = true end
        if (args["naslovHR" .. i] and args["naslovHR" .. i] ~= "") or args["RnaslovHR" .. i] or (args["naslovHR" .. i .. "dio1"] and args["naslovHR" .. i .. "dio1"] ~= "") then flags.hasNaslovHR = true end
        if (args["producent" .. i] and args["producent" .. i] ~= "") or (args["producent" .. i .. "dio1"] and args["producent" .. i .. "dio1"] ~= "") then flags.hasProducent = true end
        if (args["redatelj" .. i] and args["redatelj" .. i] ~= "") or (args["režija" .. i] and args["režija" .. i] ~= "") or (args["redatelj" .. i .. "dio1"] and args["redatelj" .. i .. "dio1"] ~= "") then flags.hasRedatelj = true end
        if (args["scenarist" .. i] and args["scenarist" .. i] ~= "") or (args["scenarij" .. i] and args["scenarij" .. i] ~= "") or (args["scenarist" .. i .. "dio1"] and args["scenarist" .. i .. "dio1"] ~= "") then flags.hasScenarist = true end
        
        local flagKeys = { 
            auxA = "hasAuxA", auxB = "hasAuxB", auxC = "hasAuxC", 
            auxD = "hasAuxD", auxE = "hasAuxE", gledanost = "hasGledanost"
        }
        for key, flag in pairs(flagKeys) do 
            if (args[key .. i] and args[key .. i] ~= "") or (args[key .. i .. "dio1"] and args[key .. i .. "dio1"] ~= "") then flags[flag] = true 
            end 
        end
    end

    -- Dohvaća tekst zaglavlja
    local function getText(key, default)
        return (args[key] and args[key] ~= "") and args[key] or default
    end

    -- Dohvaća širinu stupca
    local function getWidth(key, default)
        local raw = args[key]
        if not raw or raw == "" then return default end
        if mw.ustring.match(raw, "%%$") then
            return raw
        end

        local val = tonumber(raw)
        if val and val >= 1 and val <= 100 then
            return val .. "%" 
        end 
        return default 
    end

    -- Stvara reference za zaglavlje
    local function buildReferenceSpan(reference)
        if not reference or reference == "" then return "" end
        local span = mw.html.create("span"):wikitext(reference)
        span
            :css("color", "black")
            :css("background-color", "white")
            :css("padding", "1px")
            :css("display", "inline-block")
            :css("line-height", "50%")
        return "&#8202;" .. tostring(span)
    end

    -- Generiranje zaglavlja tablice
    local headers = {}

    if flags.bothUsed then
        table.insert(headers, {
            text   = getText("tekst brojsve", "<abbr title='Broj epizode'>Br.</abbr> u<br><span style='white-space:nowrap'>seriji</span>"),
            width  = getWidth("širina brojsve", "7%"),
            key    = "brojsve",
            shaded = true
        })
        table.insert(headers, {
            text   = getText("tekst broj", "<abbr title='Broj epizode'>Br.</abbr> u <br><span style='white-space:nowrap'>sezoni</span>"),
            width  = getWidth("širina broj", "7%"),
            key    = "broj",
            shaded = false
        })
    elseif flags.useBrojsve then
        table.insert(headers, {
            text   = getText("tekst brojsve", "<abbr title='Broj epizode'>Br.</abbr>"),
            width  = getWidth("širina brojsve", "7%"),
            key    = "brojsve",
            shaded = true
        })
    elseif flags.useBroj then
        local shaded = not flags.useBrojsve
        table.insert(headers, {
            text   = getText("tekst broj", "<abbr title='Broj epizode'>Br.</abbr>"),
            width  = getWidth("širina broj", "7%"),
            key    = "broj",
            shaded = shaded
        })
    end

    -- Dodatni stupci zaglavlja
    local optionalHeaders = {
        { flag = "hasAuxA",      textKey = "tekst auxA",      defaultText = "auxA",               widthKey = "širina auxA",      defaultWidth = "15%", key = "auxA" },
        { flag = "hasNaslov",    textKey = "tekst naslov",    defaultText = "Naslov",             widthKey = "širina naslov",    defaultWidth = "35%", key = "naslov" },
        { flag = "hasNaslovHR",  textKey = "tekst naslovHR",  defaultText = "Hrvatski naslov",    widthKey = "širina naslovHR",  defaultWidth = getWidth("širina naslov", "35%"), key = "naslovHR" },
        { flag = "hasGosti",     textKey = "tekst gosti",     defaultText = "Gosti",              widthKey = "širina gosti",     defaultWidth = "20%", key = "gosti" },
        { flag = "hasAuxB",      textKey = "tekst auxB",      defaultText = "auxB",               widthKey = "širina auxB",      defaultWidth = "15%", key = "auxB" },
        { flag = "hasProducent", textKey = "tekst producent", defaultText = "Producent",          widthKey = "širina producent", defaultWidth = "15%", key = "producent" },
        { flag = "hasRedatelj",  textKey = "tekst redatelj",  defaultText = args["tekst režija"] or "Redatelj", widthKey = "širina redatelj", defaultWidth = args["širina režija"] or "15%", key = "redatelj" },
        { flag = "hasScenarist", textKey = "tekst scenarist", defaultText = args["tekst scenarij"] or "Scenarist", widthKey = "širina scenarist", defaultWidth = args["širina scenarij"] or "15%", key = "scenarist" },
        { flag = "hasAuxC",      textKey = "tekst auxC",      defaultText = "auxC",               widthKey = "širina auxC",      defaultWidth = "15%", key = "auxC" },
        { flag = "hasAuxD",      textKey = "tekst auxD",      defaultText = "auxD",               widthKey = "širina auxD",      defaultWidth = "15%", key = "auxD" },
        { flag = "hasTrajanje",  textKey = "tekst trajanje",  defaultText = "Trajanje",           widthKey = "širina trajanje",  defaultWidth = "10%", key = "trajanje" },
        { flag = "hasDatum",     textKey = "tekst datum",     defaultText = flags.hasDatumHR and "Prvo prikazivanje" or "Prikazivanje", widthKey = "širina datum", defaultWidth = "25%", key = "datum" },
        { flag = "hasDatumALT",  textKey = "tekst datumALT",  defaultText = "Prikazivanje",       widthKey = "širina datumALT",  defaultWidth = "25%", key = "datumALT" },
        { flag = "hasDatumHR",   textKey = "tekst datumHR",   defaultText = "Prikazivanje u Hrvatskoj", widthKey = "širina datumHR", defaultWidth = "25%", key = "datumHR" },
        { flag = "hasProdkod",   textKey = "tekst prodkod",   defaultText = "<abbr title='Produkcijski'>Prod.</abbr><br>kôd", widthKey = "širina prodkod", defaultWidth = "5%", key = "prodkod" },
        { flag = "hasGledanost", textKey = "tekst gledanost", defaultText = "Gledanost",          widthKey = "širina gledanost", defaultWidth = "1%",  key = "gledanost" },
        { flag = "hasAuxE",      textKey = "tekst auxE",      defaultText = "auxE",               widthKey = "širina auxE",      defaultWidth = "15%", key = "auxE" },
    }

    for _, h in ipairs(optionalHeaders) do
        if flags[h.flag] then
            local label = getText(h.textKey, h.defaultText)

            if h.key == "naslov" and args["extra naslov"] and args["extra naslov"] ~= "" then
                label = label .. " " .. args["extra naslov"]
            end
            if h.key == "datum" and args["extra datum"] and args["extra datum"] ~= "" then
                label = label .. " " .. args["extra datum"]
            end
            if h.key == "datumALT" and args["extra datumALT"] and args["extra datumALT"] ~= "" then
                label = label .. " " .. args["extra datumALT"]
            end
            if h.key == "datumHR" and args["extra datumHR"] and args["extra datumHR"] ~= "" then
                label = label .. " " .. args["extra datumHR"]
            end 
            if h.key == "gledanost" and args["extra gledanost"] and args["extra gledanost"] ~= "" then
                label = label .. " " .. args["extra gledanost"]
            end

            table.insert(headers, {
                text  = label,
                width = getWidth(h.widthKey, h.defaultWidth),
                key   = h.key
            })
        end
    end

    -----------------------------------------
    ----- POČETAK TABLICE -----
    -----------------------------------------
    local numColumns = #headers
    -- Veličina fonta (75-100%)
    local fontSize = "100%"
    if args["font"] and args["font"] ~= "" then
        local rawFont = args["font"]
        local numericFont = tonumber(rawFont:match("^%d+"))
        if numericFont then
            if numericFont < 75 then numericFont = 75
            elseif numericFont > 100 then numericFont = 100
            end
            fontSize = numericFont .. "%"
        end
    end

    -- Moguće skupljanje tablice
    local collapseArg = (args["sklopi"] or args["collapse"] or ""):lower()
    local collapseClass = ""
    if collapseArg == "da" or collapseArg == "yes" then
        collapseClass = " mw-collapsible mw-collapsed"
    end

    table.insert(output, string.format('{| class="wikitable%s" style="width:%s; font-size:%s"', collapseClass, tableWidth, fontSize))

    -- Opis ispod tablice
    if args["opis"] and args["opis"] ~= "" then
        table.insert(output, "|+ " .. args["opis"])
    end

    -- Generiranje reda zaglavlja
    local headerRow = {}
    for _, h in ipairs(headers) do
        local style = string.format('background-color:%s; color:%s; width:%s', defaultColor, textColor, h.width)
        local label = h.text

        local refArgKey = headerRefKeys[h.key]
        if refArgKey then
            local ref = args[refArgKey]
            if ref and ref ~= "" then
                label = label .. buildReferenceSpan(ref)
            end
        end

        table.insert(headerRow, string.format('! style="%s" | %s', style, label))
    end
    table.insert(output, concat(headerRow, '\n'))

    -- Određivanje prikaza sažetka
    local title = mw.title.getCurrentTitle()
    local currentPage = title.text
    local targetPage = args["stranica"] or args["članak"] or args["1"]
    local showSummary = not targetPage or mw.uri.anchorEncode(currentPage) == mw.uri.anchorEncode(targetPage)

    -- Reset brojeva za novi prolaz
    lastBrojsve = nil
    lastBroj = nil

    -- Glavna petlja za ispis redaka
    for _, i in ipairs(sortedIndices) do
        -- Automatski broj epizode u seriji
        if not args["brojsve" .. i] and lastBrojsve then
            local num = tonumber(string.match(lastBrojsve, "%d+"))
            if num then
                args["brojsve" .. i] = tostring(num + 1) .. "."
            end
        end
        lastBrojsve = args["brojsve" .. i]
        
        for d = 1, 100 do
            if args["brojsve" .. i .. "dio" .. d] and args["brojsve" .. i .. "dio" .. d] ~= "" then
                lastBrojsve = args["brojsve" .. i .. "dio" .. d]
            end
        end

        -- Automatski broj epizode u sezoni
        if not args["broj" .. i] and lastBroj then
            local num = tonumber(string.match(lastBroj, "%d+"))
            if num then
                args["broj" .. i] = tostring(num + 1) .. "."
            end
        end
        lastBroj = args["broj" .. i]
        
        for d = 1, 100 do
            if args["broj" .. i .. "dio" .. d] and args["broj" .. i .. "dio" .. d] ~= "" then
                lastBroj = args["broj" .. i .. "dio" .. d]
            end
        end

        -- Odjeljak (section) između epizoda
        local odjeljakKey = "odjeljak" .. i
        local odjeljakText = args[odjeljakKey]
        if odjeljakText and odjeljakText ~= "" then
            local backgroundColor = args["odjeljakboja" .. i] or args["boja"] or args["pozadina"] or "#ccccff"
            local textColor = frame:expandTemplate{
                title = "Jači kontrast boja",
                args = { backgroundColor, "white", "black" }
            }
            local wrappedText = string.format(
                '<div style="max-width: 90vw; position: sticky; left: 0.2em; text-align:center;">%s</div>',
                odjeljakText
            )
            table.insert(output, "|-")
            table.insert(output, string.format(
                '! colspan="%d" style="background:%s; color:%s;" | %s',
                numColumns, backgroundColor, textColor, wrappedText
            ))
            table.insert(output, "|-")
        end

        -- Prikaz svih dijelova epizode
        if hasAnyContent(i) then
            local maxDio = getMaxDioAcrossKeys(i, dioAnchors)
            for d = 1, maxDio do
                local shouldRender = true  
                if d >= 3 then
                    shouldRender = false
                    for _, key in ipairs(dioAnchors) do
                        local val = args[key .. i .. "dio" .. (d - 1)]
                        if val and val ~= "" then
                            shouldRender = true
                            break
                        end
                    end
                end

                if shouldRender then
                    -- Anchor na broj epizode
                    local anchor = ""
                    local styleFix = ""
                    local rawBrojsve = ""
                    
                    if d == 1 then
                        rawBrojsve = args["brojsve" .. i] or lastBrojsve or ""
                        if rawBrojsve == "" then
                            rawBrojsve = args["broj" .. i] or ""
                        end
                    end
                    
                    if rawBrojsve == "" then
                        rawBrojsve = args["brojsve" .. i .. "dio" .. d] or ""
                        if rawBrojsve == "" then
                            rawBrojsve = args["broj" .. i .. "dio" .. d] or ""
                        end
                    end
                    
                    local anchorNum = string.match(rawBrojsve, "%d+")
                    if anchorNum then
                        anchor = string.format('id="ep%s"', anchorNum)
                        styleFix = 'style="scroll-margin-top:80px;"'
                    end

                    table.insert(output, string.format('|- %s %s class="vevent TV-epizoda"', anchor, styleFix))

                    -- Prolazak kroz sve stupce
                    for _, h in ipairs(headers) do
                        local key = h.key
                        local dioVal = getDioValue(i, key, d)
                        local value = ""

                        if dioVal then
                            local span = getRowSpan(i, key, maxDio, d)
                            local style = (key == "gosti") and "text-align:left" or "text-align:center"
                            value = dioVal

						-- Formatiranje datuma
						if key == "datum" or key == "datumHR" or key == "datumALT" then
						    style = "text-align:center; white-space:nowrap"
						    local rawDateInput = dioVal
						    value = dateModule.televizija{ args = { [1] = dioVal, [2] = "dtstart" } }
						    
						    if rawDateInput and rawDateInput ~= "" and not p.unformattedDateCategoryAdded then
						        -- expand template output so we can see the hidden span
						        local expanded = frame:preprocess(rawDateInput)
						        local dateToParse = expanded
						        local datePart = string.match(expanded, "^(.-)%s+%(([^)]+)%)s*$")
						        if datePart then dateToParse = mw.text.trim(datePart) end
						
						        local isFormatted = false
						
						        -- check for hidden ISO date inside the itvstart span
						        local hiddenISO = string.match(expanded, '>(%d%d%d%d%-%d%d%-%d%d)<')
						            or string.match(expanded, '>(%d%d%d%d%-%d%d)<')
						            or string.match(expanded, '>(%d%d%d%d)<')
						
						        if hiddenISO then
						            isFormatted = true
						        elseif mw.ustring.match(dateToParse, "^%d%d%d%d%-%d%d%-%d%d$") then
						            isFormatted = true
						        elseif mw.ustring.match(dateToParse, "^%d%d%d%d%-%d%d$") then
						            isFormatted = true
						        elseif mw.ustring.match(dateToParse, "^%d%d%d%d$") then
						            isFormatted = true
						        elseif dateModule.dohvatiISO(dateToParse) then
						            isFormatted = true
						        end
						
						        if not isFormatted and showSummary then
						            local category = frame:expandTemplate{
						                title = 'main other',
						                args = { '[[Kategorija:Popisi epizoda bez formatiranih datuma]]' }
						            }
						            table.insert(output, category)
						            p.unformattedDateCategoryAdded = true
						        end
						    end


                            -- Formatiranje naslova
                            elseif key == "naslov" or key == "naslovHR" or string.match(key, "^naslovHR%d+dio%d+$") then
                                style = "text-align:left"
                                local useQuotes = not (args["navodnici"] == "ne")
                                
                                if string.match(dioVal, "%s/%s") then
                                    local parts = mw.text.split(dioVal, "%s/%s")
                                    local quoted = {}
                                    for _, part in ipairs(parts) do
                                        table.insert(quoted, useQuotes and ('"' .. part .. '"') or part)
                                    end
                                    value = table.concat(quoted, " / ")
                                else
                                    value = useQuotes and ('"' .. dioVal .. '"') or dioVal
                                end
                                
                                local valUpper = mw.ustring.upper(dioVal or "")
                                if valUpper == "NE" or valUpper == "N/E" then
                                    value = nadolazeca()
                                end
                                
                                value = string.format('<span class="summary">%s</span>', value)
                            else
                                value = dioVal
                            end
                            
                            -- Dodavanje ćelije
                            if h.shaded then
                                if span > 1 then
                                    table.insert(output, string.format(
                                        '! rowspan="%d" style="font-weight:normal; %s" | %s',
                                        span, style, value))
                                else
                                    table.insert(output, string.format(
                                        '! style="font-weight:normal; %s" | %s',
                                        style, value))
                                end
                            else
                                if span > 1 then
                                    table.insert(output, string.format(
                                        '| rowspan="%d" style="%s" | %s',
                                        span, style, value))
                                else
                                    table.insert(output, string.format(
                                        '| style="%s" | %s',
                                        style, value))
                                end
                            end

                        -- Ako nema vrijednosti za dio, ali je prvi dio
                        elseif d == 1 then
                            if key == "naslov" then
                                local raw = buildNaslov(i)
                                if mw.text.trim(raw) == "" then
                                    value = ""
                                else
                                    value = string.format('<span class="summary">%s</span>', raw)
                                end
                            elseif key == "naslovHR" then
                                local raw = buildNaslovHR(i)
                                if mw.text.trim(raw) == "" then
                                    value = ""
                                else
                                    value = string.format('<span class="summary">%s</span>', raw)
                                end
								elseif key == "datum" or key == "datumHR" or key == "datumALT" then
								    local originalValue = getValue(i, key)
								    local rawDateInput = originalValue
								    value = dateModule.televizija{ args = { [1] = originalValue, [2] = "dtstart" } }
								    
								    if rawDateInput and rawDateInput ~= "" and not p.unformattedDateCategoryAdded then
								        -- expand template output so we can see the hidden span
								        local expanded = frame:preprocess(rawDateInput)
								        local dateToParse = expanded
								        local datePart = string.match(expanded, "^(.-)%s+%(([^)]+)%)s*$")
								        if datePart then dateToParse = mw.text.trim(datePart) end
								
								        local isFormatted = false
								
								        -- check for hidden ISO date inside the itvstart span
								        local hiddenISO = string.match(expanded, '>(%d%d%d%d%-%d%d%-%d%d)<')
								            or string.match(expanded, '>(%d%d%d%d%-%d%d)<')
								            or string.match(expanded, '>(%d%d%d%d)<')
								
								        if hiddenISO then
								            isFormatted = true
								        elseif mw.ustring.match(dateToParse, "^%d%d%d%d%-%d%d%-%d%d$") then
								            isFormatted = true
								        elseif mw.ustring.match(dateToParse, "^%d%d%d%d%-%d%d$") then
								            isFormatted = true
								        elseif mw.ustring.match(dateToParse, "^%d%d%d%d$") then
								            isFormatted = true
								        elseif dateModule.dohvatiISO(dateToParse) then
								            isFormatted = true
								        end
								
								        if not isFormatted and showSummary then
								            local category = frame:expandTemplate{
								                title = 'main other',
								                args = { '[[Kategorija:Popisi epizoda bez formatiranih datuma]]' }
								            }
								            table.insert(output, category)
								            p.unformattedDateCategoryAdded = true
								        end
								    end

                            else
                                value = getValue(i, key)
                            end

							-- Logika za prazne ćelije
							local praznoText = args["prazno"]
							local innerText = value:match('<span[^>]*>(.-)</span>')
							local isEmptySpan = innerText and mw.text.trim(innerText) == ""
							
							local isUpcoming = false
							local contentLang = mw.getContentLanguage()
							local today = contentLang:formatDate('Y-m-d')
							
							local function checkDateInFuture(dateInput)
							    if not dateInput or dateInput == "" then return false end
							    local isoDate = nil
							
							    -- Traži ISO datum iz predložaka poput {{premijera}}
							    local expanded = frame:preprocess(dateInput)
							
							    isoDate = string.match(expanded, '>(%d%d%d%d%-%d%d%-%d%d)<')
							        or string.match(expanded, '>(%d%d%d%d%-%d%d)<')
							        or string.match(expanded, '>(%d%d%d%d)<')
							
							    -- ako nema, sredi običan tekst
							    if not isoDate then
							        local dateToParse = expanded
							        local datePart = string.match(expanded, "^(.-)%s+%(([^)]+)%)s*$")
							        if datePart then dateToParse = mw.text.trim(datePart) end
							        isoDate = mw.ustring.match(dateToParse, "(%d%d%d%d%-%d%d%-%d%d)")
							        if not isoDate then isoDate = dateModule.dohvatiISO(dateToParse) end
							    end
							
							    if isoDate then return isoDate > today end
							    return false
							end
							
							-- provjera je li datum u budućnosti
							if not isUpcoming then
							    local rawDateParam = args["datum" .. i]
							    if rawDateParam and rawDateParam ~= "" then
							        isUpcoming = checkDateInFuture(rawDateParam)
							    end
							end
							
							if not isUpcoming then
							    for dCheck = 1, 10 do
							        local rawDateParam = args["datum" .. i .. "dio" .. dCheck]
							        if rawDateParam and rawDateParam ~= "" then
							            if checkDateInFuture(rawDateParam) then
							                isUpcoming = true
							                break
							            end
							        end
							    end
							end
							
							-- naslovHR dobiva N/E ako je datumHR u budućnosti
							local rawDateHR = args["datumHR" .. i]
							local rawTitleHR = args["naslovHR" .. i]
							if rawDateHR and checkDateInFuture(rawDateHR) then
							    if (not rawTitleHR or mw.text.trim(rawTitleHR) == "") and key == "naslovHR" and (not praznoText or praznoText == "") then
							        value = "N/E"
							    end
							end
							
							-- Kategorija za nadolazeće epizode (uvijek ako je datum u budućnosti)
							if isUpcoming and showSummary and not p.upcomingCategoryAdded then
							    local category = frame:expandTemplate{ 
							        title = 'main other', 
							        args = { '[[Kategorija:Stranice s nadolazećim televizijskim epizodama]]' } 
							    }
							    table.insert(output, category)
							    p.upcomingCategoryAdded = true
							end
							
						-- Logika za praznoText
						if not praznoText or praznoText == "" then
						    if (not value or value == "" or isEmptySpan) then
						        if isUpcoming then
						            value = nadolazeca()
						        else
						            value = nemapodataka()
						            if showSummary and not p.incompleteCategoryAdded then
						                local category = frame:expandTemplate{ 
						                    title = 'main other', 
						                    args = { '[[Kategorija:Nepotpuni popisi epizoda]]' } 
						                }
						                table.insert(output, category)
						                p.incompleteCategoryAdded = true
						            end
						        end
						    end
							else
							    if (not value or value == "" or isEmptySpan) then
							        if praznoText == "NE" or praznoText == "N/E" then
							            value = nadolazeca()
							        else
							            value = string.format('<span class="skin-nightmode-reset-color tv-tba" style="background:#eceff2; padding:1px 5px; border-radius:7px; display:inline-block; font-size:88%%">%s</span>', praznoText)
							        end
							    end
							end
							
							if (not praznoText or praznoText == "") and (value == "NE" or value == "N/E") then
							    value = nadolazeca()
							end
							
							local span = getRowSpan(i, key, maxDio, d)
							local style = (key == "gosti") and "text-align:left" or ""
							
							if key == "broj" or key == "producent" or key == "redatelj" or key == "scenarist" or key == "gledanost"
							   or key == "auxA" or key == "auxB" or key == "auxC" or key == "auxD" or key == "auxE" or key == "trajanje" or key == "prodkod" then
							    style = "text-align:center"
							elseif key == "datum" or key == "datumHR" or key == "datumALT" then
							    style = "text-align:center; white-space:nowrap"
							end
							
							if h.shaded then
							    if span > 1 then
							        table.insert(output, string.format(
							            '! rowspan="%d" style="font-weight:normal; %s" | %s',
							            span, style, value))
							    else
							        table.insert(output, string.format(
							            '! style="font-weight:normal; %s" | %s',
							            style, value))
							    end
							else
							    if span > 1 then
							        table.insert(output, string.format(
							            '| rowspan="%d" style="%s" | %s',
							            span, style, value))
							    else
							        table.insert(output, string.format(
							            '| style="%s" | %s',
							            style, value))
							    end
							end

						end 
                    end
                end
            end
        end

        -- Kratki sadržaj
        local sadrzaj = args["kratkisadržaj" .. i] or args["sadržaj" .. i] or ""
        if showSummary and sadrzaj ~= "" then
            local nextOdjeljak = args["odjeljak" .. (i + 1)]
            local style = ""
            if not nextOdjeljak then
                local lineColor = args["bojalinije" .. i] or defaultColor
                style = string.format('style="border-bottom:3px solid %s"', lineColor)
            end
            local wrapped = string.format(
                '<div style="max-width: 90vw; position: sticky; left: 0.2em;">%s</div>', sadrzaj)
            table.insert(output, string.format(
                '|- \n| colspan="%d" %s | %s', numColumns, style, wrapped))
        end
    end
    
    table.insert(output, "|}")
    return concat(output, "\n")
end

return p