Modulo:ChronoSpine/render
Vai alla navigazione
Vai alla ricerca
La documentazione per questo modulo può essere creata in Modulo:ChronoSpine/render/man
--[[
Module:ChronoSpine/render
============================
Purpose
-------
Turns the ordered group list produced by Module:ChronoSpine/group
into the actual spine wikitext, using `mw.html` (never raw string
concatenation) and the CSS classes defined in
Template:ChronoSpine/styles.css.
This module never assembles Italian text itself: every piece of
display text (a formatted date, a stardate, a group heading) comes
from Module:ChronoSpine/i18n, and every site-specific name (a year's
link target, an icon file) comes from Module:ChronoSpine/config. This
module only decides STRUCTURE -- which `<div>`s, which classes, in
which order -- never wording.
Two public functions, deliberately kept separate:
* M.render(groups, options) -- the main spine.
* M.problems_notice(problems) -- a small notice + tracking category,
built from Module:ChronoSpine/manual's
`problems` list. Kept apart from
M.render so a future adapter with a
different (or no) notion of
"problem" is never forced through
this exact path; the top-level
module (Step 5) is what actually
concatenates the two outputs.
What this version deliberately does NOT do yet (see the Step 4 design
discussion for the full reasoning on each):
* `comprimi` (collapsing empty-year runs) -- would require
synthesising placeholder groups, which Module:ChronoSpine/group
explicitly does not do.
* Any entry collapsing/`espandi` handling -- every entry in every
group is rendered in full, always, for now.
* `layout = tracce` -- silently falls back to the one layout this
module knows how to draw.
* The reale-mode "missing Italian date" fallback/footer marker --
needs a per-entry flag the current entry contract does not have
(that only arrives with the SMW adapter, Step 7).
--]]
local M = {}
local i18n = require("Module:ChronoSpine/i18n")
local config = require("Module:ChronoSpine/config")
--[[
is_hollow(e)
-------------
Private helper. An entry gets the hollow, dashed dot (§5 of the Step 4
design discussion) when it is explicitly marked "announced" (reale or
narrativo alike -- treated as a generic entry attribute, not a
reale-only concept), OR when it is an uncertain-date entry.
Checking `e.incerta` directly (rather than the group's `.kind`) is
deliberate and simpler: an entry's own `incerta` flag already tells
us exactly what Module:ChronoSpine/group used to decide whether it
went into the trailing "Data incerta" group in the first place -- the
two can never disagree, so there is no need to thread the group's
kind down into this function separately.
--]]
local function is_hollow(e)
return e.stato == "annunciato" or e.incerta
end
--[[
resolve_icon_wikitext(categoria)
-----------------------------------
Private helper. Looks up `categoria` in Module:ChronoSpine/config's
M.category_icons.
Parameters:
categoria (string|nil)
Returns:
(string|nil) -- the icon's ready-to-use wikitext, or nil if
`categoria` is nil, or is set but not found in the
config table. Either way, render_entry() below
leaves `.chronospine-entry-icon` empty in that case,
and styles.css draws a plain CSS dot there instead --
a typo in an editor's `categoria=` value degrades
gracefully rather than producing a broken link.
--]]
local function resolve_icon_wikitext(categoria)
if not categoria then
return nil
end
return config.category_icons[categoria]
end
--[[
render_entry(parent, e, tempo)
---------------------------------
Private helper. Appends one `.chronospine-entry` node (icon + body:
date, stardate, label, note, source) to `parent`.
Parameters:
parent (table) -- an mw.html node (the group's container) to
append this entry's node to.
e (table) -- one normalised entry (Module:ChronoSpine/entry).
tempo (string|nil) -- "reale" or "narrativo"; controls whether the
stardate line is shown at all (narrativo only,
per the project briefing).
--]]
local function render_entry(parent, e, tempo)
local entry_node = parent:tag("div"):addClass("chronospine-entry")
if is_hollow(e) then
entry_node:addClass("chronospine-entry--hollow")
end
local icon_node = entry_node:tag("div"):addClass("chronospine-entry-icon")
local icon_wikitext = resolve_icon_wikitext(e.categoria)
if icon_wikitext then
icon_node:wikitext(icon_wikitext)
end
local body = entry_node:tag("div"):addClass("chronospine-entry-body")
-- An `incerta` entry has no anno/mese/giorno at all (per
-- Module:ChronoSpine/entry's contract) -- there is nothing to
-- format, so the date line is omitted entirely rather than
-- calling i18n.format_date on an empty date.
if not e.incerta then
body:tag("div"):addClass("chronospine-entry-date"):wikitext(i18n.format_date(e))
end
if tempo == "narrativo" and e.stellare then
body:tag("div"):addClass("chronospine-entry-stardate")
:wikitext(i18n.format_stardate(e.stellare))
end
body:tag("div"):addClass("chronospine-entry-label"):wikitext(e.voce)
if e.nota then
body:tag("div"):addClass("chronospine-entry-note"):wikitext(e.nota)
end
if e.fonte then
body:tag("div"):addClass("chronospine-entry-source"):wikitext(e.fonte)
end
end
--[[
render_group(parent, group, options)
---------------------------------------
Private helper. Appends one `.chronospine-group` node (heading marker
+ its entries) to `parent`.
Year auto-linking: only when this group is a normal "anno"-mode group
with a year, AND Module:ChronoSpine/config has a link pattern for the
current `tempo` ("Eventi %d" for reale, "Timeline %d" for narrativo --
see that module). Anything else (a different `raggruppa`, the
`incerta` group, or a future `tempo` value not in the config table)
falls back to plain, unlinked heading text.
`stagione` mode's heading text comes from the entry-supplied `gruppo`
field, which -- unlike `voce`/`fonte`/`nota` -- is NOT documented as
allowing wikitext, so it is passed through `mw.text.nowiki()` here
before being used as a heading. "anno"/"mese"/"giorno" headings and
the fixed "Data incerta"/"Senza gruppo" strings never need this: they
are either plain numbers/month names or our own trusted text.
Parameters:
parent (table) -- an mw.html node (the spine container).
group (table) -- one group, as produced by Module:ChronoSpine/group.
options (table) -- the options passed to M.render (see its docstring).
--]]
local function render_group(parent, group, options)
local group_node = parent:tag("div"):addClass("chronospine-group")
if group.kind == "incerta" then
group_node:addClass("chronospine-group--incerta")
end
local label_text = i18n.group_label(group.key, options.raggruppa, group.kind)
local marker = group_node:tag("div"):addClass("chronospine-group-marker")
local linked = false
if group.kind == "normal" and options.raggruppa == "anno" and group.key.anno then
local pattern = config.year_link_pattern[options.tempo]
if pattern then
local target = string.format(pattern, group.key.anno)
marker:wikitext(string.format("[[%s|%s]]", target, label_text))
linked = true
end
end
if not linked then
if options.raggruppa == "stagione" then
marker:wikitext(mw.text.nowiki(label_text))
else
marker:wikitext(label_text)
end
end
for _, e in ipairs(group.entries) do
render_entry(group_node, e, options.tempo)
end
end
--[[
M.render(groups, options)
----------------------------
The main entry point: builds the whole spine.
Parameters:
groups (table) -- the ordered group list from
Module:ChronoSpine/group's M.group().
options (table|nil) -- {
tempo = "reale"|"narrativo",
raggruppa = "anno"|"mese"|"giorno"|"stagione",
titolo = string|nil,
allinea = "destra"|"sinistra"|nil,
}
(the same `raggruppa` value that was passed
to Module:ChronoSpine/group's M.group() for
these `groups` -- this module trusts the
caller to pass the matching one, rather
than re-deriving it from the data).
Returns:
(string) -- wikitext for the whole spine box. Does NOT include the
problems notice or tracking category -- see
M.problems_notice below.
--]]
function M.render(groups, options)
options = options or {}
local box = mw.html.create("div"):addClass("chronospine-box")
if options.allinea == "sinistra" then
box:addClass("chronospine-box--left")
end
if options.titolo then
box:tag("div"):addClass("chronospine-title"):wikitext(options.titolo)
end
local spine = box:tag("div"):addClass("chronospine-spine")
for _, group in ipairs(groups) do
render_group(spine, group, options)
end
return tostring(box)
end
--[[
M.problems_notice(problems)
------------------------------
Builds the visible maintenance notice for Module:ChronoSpine/manual's
`problems` list, if any -- one line per problem (via
Module:ChronoSpine/i18n's M.message), plus the tracking category from
Module:ChronoSpine/config's M.problem_category.
Each message is passed through `mw.text.nowiki()` before being used as
wikitext. This matters because a problem's underlying raw text (e.g.
an unparseable date an editor typed) can itself contain wiki markup
and so on -- and that raw text ends up embedded inside
the message by Module:ChronoSpine/i18n's M.message. Escaping the
WHOLE resulting message is safe (the fixed Italian wording around it
is plain text with nothing that needs to render as markup) and is
what actually neutralises anything an editor's raw input might
otherwise inject into the page.
Parameters:
problems (table|nil) -- as produced by Module:ChronoSpine/manual's
M.build() (the `.problems` field).
Returns:
(string|nil) -- wikitext for the notice + category, or `nil` if
`problems` is nil or empty (nothing to show, and
nothing to add the page to).
--]]
function M.problems_notice(problems)
if not problems or #problems == 0 then
return nil
end
local box = mw.html.create("div"):addClass("chronospine-problems")
box:tag("p"):wikitext("'''ChronoSpine: problemi rilevati nei dati.'''")
local list = box:tag("ul")
for _, problem in ipairs(problems) do
list:tag("li"):wikitext(mw.text.nowiki(i18n.message(problem)))
end
return tostring(box) .. "[[" .. config.problem_category .. "]]"
end
return M