<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="es">
	<id>https://wikisp.org/wiki/index.php?action=history&amp;feed=atom&amp;title=M%C3%B3dulo%3ACountdown</id>
	<title>Módulo:Countdown - Historial de revisiones</title>
	<link rel="self" type="application/atom+xml" href="https://wikisp.org/wiki/index.php?action=history&amp;feed=atom&amp;title=M%C3%B3dulo%3ACountdown"/>
	<link rel="alternate" type="text/html" href="https://wikisp.org/wiki/index.php?title=M%C3%B3dulo:Countdown&amp;action=history"/>
	<updated>2026-09-14T05:21:59Z</updated>
	<subtitle>Historial de revisiones de esta página en la wiki</subtitle>
	<generator>MediaWiki 1.46.0</generator>
	<entry>
		<id>https://wikisp.org/wiki/index.php?title=M%C3%B3dulo:Countdown&amp;diff=8138&amp;oldid=prev</id>
		<title>Luxbane: Página creada con «require(&#039;strict&#039;)  --- @module Countdown --- A live countdown: the hook a gadget animates, plus a fallback that is true --- without JavaScript. --- --- It deliberately does NOT compute the remaining time here. A value worked out --- at parse time is frozen into the parser cache and can be arbitrarily stale — --- and a countdown reading &quot;2 days left&quot; for an event that finished last week is --- worse than no countdown at all. So the dates go out as data attributes, th…»</title>
		<link rel="alternate" type="text/html" href="https://wikisp.org/wiki/index.php?title=M%C3%B3dulo:Countdown&amp;diff=8138&amp;oldid=prev"/>
		<updated>2026-09-11T23:25:39Z</updated>

		<summary type="html">&lt;p&gt;Página creada con «require(&amp;#039;strict&amp;#039;)  --- @module Countdown --- A live countdown: the hook a gadget animates, plus a fallback that is true --- without JavaScript. --- --- It deliberately does NOT compute the remaining time here. A value worked out --- at parse time is frozen into the parser cache and can be arbitrarily stale — --- and a countdown reading &amp;quot;2 days left&amp;quot; for an event that finished last week is --- worse than no countdown at all. So the dates go out as data attributes, th…»&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Página nueva&lt;/b&gt;&lt;/p&gt;&lt;div&gt;require(&amp;#039;strict&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
--- @module Countdown&lt;br /&gt;
--- A live countdown: the hook a gadget animates, plus a fallback that is true&lt;br /&gt;
--- without JavaScript.&lt;br /&gt;
---&lt;br /&gt;
--- It deliberately does NOT compute the remaining time here. A value worked out&lt;br /&gt;
--- at parse time is frozen into the parser cache and can be arbitrarily stale —&lt;br /&gt;
--- and a countdown reading &amp;quot;2 days left&amp;quot; for an event that finished last week is&lt;br /&gt;
--- worse than no countdown at all. So the dates go out as data attributes, the&lt;br /&gt;
--- mainpage gadget does the arithmetic on load, and a reader without JavaScript&lt;br /&gt;
--- sees the date range, which stays true forever.&lt;br /&gt;
---&lt;br /&gt;
--- The module owns the markup and the styling; the gadget owns the numbers. That&lt;br /&gt;
--- is the same division Module:Entity&amp;#039;s stat line uses, where the server renders&lt;br /&gt;
--- a plausible figure and the gadget corrects it — except here the honest&lt;br /&gt;
--- fallback is a different fact rather than a stale version of the same one.&lt;br /&gt;
&lt;br /&gt;
local START_ATTR = &amp;#039;data-gadget-mainpage-countdown-start&amp;#039;&lt;br /&gt;
local END_ATTR = &amp;#039;data-gadget-mainpage-countdown-end&amp;#039;&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
--- @param iso string&lt;br /&gt;
--- @return string|nil formatted, or nil when the date cannot be read&lt;br /&gt;
local function formatDate(iso, format)&lt;br /&gt;
	if not iso or iso == &amp;#039;&amp;#039; then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	local ok, formatted = pcall(function()&lt;br /&gt;
		return mw.language.getContentLanguage():formatDate(format, iso)&lt;br /&gt;
	end)&lt;br /&gt;
	return ok and formatted or nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Rejects a date the formatter cannot read. A typo has to be loud: silently&lt;br /&gt;
--- rendering nothing is the one outcome nobody notices, and this element&amp;#039;s whole&lt;br /&gt;
--- job is to be the page&amp;#039;s most conspicuous statement about time.&lt;br /&gt;
---&lt;br /&gt;
--- @param name string parameter name, for the message&lt;br /&gt;
--- @param iso string&lt;br /&gt;
local function assertReadable(name, iso)&lt;br /&gt;
	if not formatDate(iso, &amp;#039;j F Y&amp;#039;) then&lt;br /&gt;
		error(&amp;#039;Countdown: &amp;#039; .. name .. &amp;#039; is not a readable date: &amp;#039; .. tostring(iso))&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- The no-JS reading: a date, or a range when both ends are known. The year is&lt;br /&gt;
--- dropped from the opening date when both fall in the same one, so the common&lt;br /&gt;
--- case reads &amp;quot;12 – 18 August 2026&amp;quot; rather than repeating itself.&lt;br /&gt;
---&lt;br /&gt;
--- @param starts string|nil&lt;br /&gt;
--- @param ends string&lt;br /&gt;
--- @return string&lt;br /&gt;
local function fallbackText(starts, ends)&lt;br /&gt;
	local endText = formatDate(ends, &amp;#039;j F Y&amp;#039;)&lt;br /&gt;
	if not endText then&lt;br /&gt;
		return &amp;#039;&amp;#039;&lt;br /&gt;
	end&lt;br /&gt;
	if not starts or starts == &amp;#039;&amp;#039; then&lt;br /&gt;
		return endText&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local sameYear = formatDate(starts, &amp;#039;Y&amp;#039;) == formatDate(ends, &amp;#039;Y&amp;#039;)&lt;br /&gt;
	local startText = formatDate(starts, sameYear and &amp;#039;j F&amp;#039; or &amp;#039;j F Y&amp;#039;)&lt;br /&gt;
	if not startText then&lt;br /&gt;
		return endText&lt;br /&gt;
	end&lt;br /&gt;
	return startText .. &amp;#039; – &amp;#039; .. endText&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- @class CountdownProps&lt;br /&gt;
--- @field starts? string  ISO 8601 start. Omit for something already under way;&lt;br /&gt;
---        with it the gadget can also render the &amp;quot;starts in&amp;quot; tense.&lt;br /&gt;
--- @field ends string     ISO 8601 end. Required.&lt;br /&gt;
--- @field label? string   Overrides the gadget&amp;#039;s tense-derived label. Rarely&lt;br /&gt;
---        wanted — the whole point is that the label follows the dates.&lt;br /&gt;
--- @field class? string&lt;br /&gt;
&lt;br /&gt;
--- @param props CountdownProps&lt;br /&gt;
--- @return string&lt;br /&gt;
function p.render(props)&lt;br /&gt;
	if not props.ends or props.ends == &amp;#039;&amp;#039; then&lt;br /&gt;
		error(&amp;#039;Countdown: ends is required&amp;#039;)&lt;br /&gt;
	end&lt;br /&gt;
	assertReadable(&amp;#039;ends&amp;#039;, props.ends)&lt;br /&gt;
	if props.starts and props.starts ~= &amp;#039;&amp;#039; then&lt;br /&gt;
		assertReadable(&amp;#039;starts&amp;#039;, props.starts)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local styles = mw.getCurrentFrame():extensionTag({&lt;br /&gt;
		name = &amp;#039;templatestyles&amp;#039;,&lt;br /&gt;
		args = { src = &amp;#039;Module:Countdown/styles.css&amp;#039; },&lt;br /&gt;
	})&lt;br /&gt;
&lt;br /&gt;
	local root = mw.html.create(&amp;#039;div&amp;#039;):addClass(&amp;#039;t-countdown&amp;#039;)&lt;br /&gt;
	if props.class and props.class ~= &amp;#039;&amp;#039; then&lt;br /&gt;
		root:addClass(props.class)&lt;br /&gt;
	end&lt;br /&gt;
	root:attr(END_ATTR, props.ends)&lt;br /&gt;
	if props.starts and props.starts ~= &amp;#039;&amp;#039; then&lt;br /&gt;
		root:attr(START_ATTR, props.starts)&lt;br /&gt;
	end&lt;br /&gt;
	if props.label and props.label ~= &amp;#039;&amp;#039; then&lt;br /&gt;
		root:attr(&amp;#039;data-gadget-mainpage-countdown-label&amp;#039;, props.label)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- The fallback is a real sentence-worth of fact, not an empty shell: if the&lt;br /&gt;
	-- gadget never runs, this is what the reader is left with.&lt;br /&gt;
	root:tag(&amp;#039;div&amp;#039;):addClass(&amp;#039;t-countdown__fallback&amp;#039;):wikitext(fallbackText(props.starts, props.ends))&lt;br /&gt;
&lt;br /&gt;
	return styles .. tostring(root)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Wikitext entry point.&lt;br /&gt;
---&lt;br /&gt;
--- @param frame mw.frame&lt;br /&gt;
--- @return string&lt;br /&gt;
function p.main(frame)&lt;br /&gt;
	local args = require(&amp;#039;Module:Arguments&amp;#039;).getArgs(frame)&lt;br /&gt;
	return p.render({&lt;br /&gt;
		starts = args.starts,&lt;br /&gt;
		ends = args.ends,&lt;br /&gt;
		label = args.label,&lt;br /&gt;
		class = args.class,&lt;br /&gt;
	})&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Luxbane</name></author>
	</entry>
</feed>