Module:Television Series Rating
Appearance
local p = {}
--------------------------------------------------
-- Rating → background + text color (EPISODE CELLS ONLY)
--------------------------------------------------
local function ratingStyle(r)
if not r then
return nil, nil
end
if r >= 9.6 then
return "#1C89F4", "#ffffff"
elseif r >= 9.0 then
return "#037732", "#ffffff"
elseif r >= 7.5 then
return "#00C853", "#000000"
elseif r >= 6.0 then
return "#FFD600", "#000000"
elseif r >= 4.1 then
return "#E03733", "#ffffff"
else
return "#8B00FF", "#ffffff"
end
end
--------------------------------------------------
-- Collect seasons dynamically
--------------------------------------------------
local function getSeasons(args)
local seasons = {}
local i = 1
while args["season" .. i] do
seasons[i] = mw.text.split(args["season" .. i], ",")
i = i + 1
end
return seasons
end
--------------------------------------------------
-- Main render
--------------------------------------------------
function p.render(frame)
local args = frame:getParent() and frame:getParent().args or frame.args
local seasons = getSeasons(args)
if #seasons == 0 then
return "Error: No seasons provided."
end
--------------------------------------------------
-- OUTER WRAPPER (skin-owned)
--------------------------------------------------
local wrapper = mw.html.create("div")
:css({
["max-width"] = "100%",
["overflow-x"] = "auto",
["margin"] = "1em auto",
["text-align"] = "center",
["font-family"] =
'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif'
})
--------------------------------------------------
-- TITLE + OVERALL RATING (skin colors)
--------------------------------------------------
if args.title or args.overall_rating then
local header = wrapper:tag("div")
:css({ ["margin-bottom"] = "12px" })
if args.title then
header:tag("span")
:css({
["font-size"] = "clamp(1.6em, 3vw, 2.4em)",
["font-weight"] = "800",
["margin-right"] = "12px"
})
:wikitext(args.title)
end
if args.overall_rating then
local t = "★ " .. args.overall_rating
if args.overall_votes then
t = t .. " (" .. args.overall_votes .. ")"
end
header:tag("span")
:css({
["font-size"] = "clamp(1.1em, 2vw, 1.5em)",
["font-weight"] = "600",
["color"] = "#FFD600"
})
:wikitext(t)
end
end
--------------------------------------------------
-- TABLE (NO BACKGROUND)
--------------------------------------------------
local tableRoot = mw.html.create("table")
:css({
["border-collapse"] = "separate",
["border-spacing"] = "6px",
["margin"] = "0 auto",
["text-align"] = "center"
})
--------------------------------------------------
-- HEADER ROW
--------------------------------------------------
local headerRow = tableRoot:tag("tr")
headerRow:tag("th")
for i = 1, #seasons do
headerRow:tag("th")
:css({
["padding"] = "10px 16px",
["border-radius"] = "6px",
["font-weight"] = "700",
["font-size"] = "clamp(1.2em, 2vw, 1.9em)"
})
:wikitext("S" .. i)
end
--------------------------------------------------
-- ACCUMULATORS
--------------------------------------------------
local sum, count = {}, {}
for i = 1, #seasons do
sum[i], count[i] = 0, 0
end
local maxEpisodes = 0
for _, eps in ipairs(seasons) do
if #eps > maxEpisodes then
maxEpisodes = #eps
end
end
--------------------------------------------------
-- EPISODE ROWS
--------------------------------------------------
for e = 1, maxEpisodes do
local row = tableRoot:tag("tr")
row:tag("th")
:css({
["padding"] = "10px 16px",
["font-weight"] = "700",
["font-size"] = "clamp(1.2em, 2vw, 1.9em)"
})
:wikitext("E" .. e)
for s, eps in ipairs(seasons) do
local rating = tonumber(eps[e])
local cell = row:tag("td")
if rating then
local bg, fg = ratingStyle(rating)
cell
:css({
["background"] = bg,
["color"] = fg,
["padding"] = "clamp(10px, 1.5vw, 18px)",
["border-radius"] = "8px",
["font-weight"] = "800",
["font-size"] = "clamp(1.6em, 3vw, 2.5em)",
["line-height"] = "1",
["min-width"] = "clamp(42px, 6vw, 60px)"
})
:wikitext(string.format("%.1f", rating))
sum[s] = sum[s] + rating
count[s] = count[s] + 1
else
cell:css({ ["opacity"] = "0" })
end
end
end
--------------------------------------------------
-- SEASON AVERAGES (PLAIN TEXT, SKIN-CONTROLLED)
--------------------------------------------------
local avgRow = tableRoot:tag("tr")
avgRow:tag("th")
for s = 1, #seasons do
local cell = avgRow:tag("td")
if count[s] > 0 then
local avg = sum[s] / count[s]
cell
:css({
["padding"] = "clamp(8px, 1.5vw, 14px)",
["border-radius"] = "6px",
["font-weight"] = "700",
["font-size"] = "clamp(1.3em, 2.5vw, 2.1em)"
})
:wikitext(string.format("%.1f", avg))
else
cell:css({ ["opacity"] = "0" })
end
end
wrapper:node(tableRoot)
--------------------------------------------------
-- LEGEND (SKIN-CONTROLLED)
--------------------------------------------------
local legend = wrapper:tag("div")
:css({
["margin-top"] = "14px",
["font-size"] = "clamp(0.9em, 1.5vw, 1.2em)",
["font-weight"] = "600",
["text-align"] = "center"
})
local function legendItem(color, label)
local item = legend:tag("span")
:css({ ["margin"] = "0 10px", ["white-space"] = "nowrap" })
item:tag("span")
:css({
["display"] = "inline-block",
["width"] = "16px",
["height"] = "16px",
["background"] = color,
["border-radius"] = "4px",
["margin-right"] = "6px",
["vertical-align"] = "middle"
})
item:tag("span")
:css({ ["vertical-align"] = "middle" })
:wikitext(label)
end
legendItem("#1C89F4", "PERFECTION")
legendItem("#037732", "EXCELLENT")
legendItem("#00C853", "GREAT")
legendItem("#FFD600", "DECENT")
legendItem("#E03733", "BAD")
legendItem("#8B00FF", "GARBAGE")
return tostring(wrapper)
end
return p