<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://interfaces3.hunter.cryptograss.live/index.php?action=history&amp;feed=atom&amp;title=Module%3ASrc</id>
	<title>Module:Src - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://interfaces3.hunter.cryptograss.live/index.php?action=history&amp;feed=atom&amp;title=Module%3ASrc"/>
	<link rel="alternate" type="text/html" href="https://interfaces3.hunter.cryptograss.live/index.php?title=Module:Src&amp;action=history"/>
	<updated>2026-11-15T06:35:43Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.43.6</generator>
	<entry>
		<id>https://interfaces3.hunter.cryptograss.live/index.php?title=Module:Src&amp;diff=5412&amp;oldid=prev</id>
		<title>Magent: Drop the literal brackets — wiki links already carry ... and the extra pair broke parsing (via update-page on MediaWiki MCP Server)</title>
		<link rel="alternate" type="text/html" href="https://interfaces3.hunter.cryptograss.live/index.php?title=Module:Src&amp;diff=5412&amp;oldid=prev"/>
		<updated>2026-08-16T22:05:31Z</updated>

		<summary type="html">&lt;p&gt;Drop the literal brackets — wiki links already carry &lt;a href=&quot;/index.php?title=...&amp;amp;action=edit&amp;amp;redlink=1&quot; class=&quot;new&quot; title=&quot;... (page does not exist)&quot;&gt;...&lt;/a&gt; and the extra pair broke parsing (via update-page on MediaWiki MCP Server)&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 22:05, 16 August 2026&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l272&quot;&gt;Line 272:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 272:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;--- Render an error in place, visibly but without killing the page.&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;--- Render an error in place, visibly but without killing the page.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;local function errorSpan( message )&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;local function errorSpan( message )&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	return &#039;&amp;lt;span class=&quot;error pp-src-error&quot;&amp;gt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;[&lt;/del&gt;Src: &#039; .. &lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;mw.text.nowiki( &lt;/del&gt;message &lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;) &lt;/del&gt;.. &#039;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;]&lt;/del&gt;&amp;lt;/span&amp;gt;&#039;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	return &#039;&amp;lt;span class=&quot;error pp-src-error&quot;&amp;gt;Src: &#039; .. message .. &#039;&amp;lt;/span&amp;gt;&#039;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;end&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;end&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l316&quot;&gt;Line 316:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 316:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	end&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	end&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	return &#039;&amp;lt;sup class=&quot;pp-src pp-src-&#039; .. kind .. &#039;&quot;&amp;gt;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;[&lt;/del&gt;&#039; .. result.link .. &#039;&lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;]&lt;/del&gt;&amp;lt;/sup&amp;gt;&#039;&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;	-- No literal brackets around the link. Wiki links already arrive wrapped&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;	-- in [[...]], and a second pair produced [[[...]]], which the parser gives&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;	-- up on and prints raw. Brackets are a styling concern anyway — .pp-src&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;	-- can draw them in CSS without touching the link syntax.&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;	return &#039;&amp;lt;sup class=&quot;pp-src pp-src-&#039; .. kind .. &#039;&quot;&amp;gt;&#039; .. result.link .. &#039;&amp;lt;/sup&amp;gt;&#039;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;		.. subobject( frame, kind, result.props )&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;		.. subobject( frame, kind, result.props )&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;end&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;end&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;return p&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;return p&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</summary>
		<author><name>Magent</name></author>
	</entry>
	<entry>
		<id>https://interfaces3.hunter.cryptograss.live/index.php?title=Module:Src&amp;diff=5410&amp;oldid=prev</id>
		<title>Magent: Src: inline citations that are also queryable subobjects (via create-page on MediaWiki MCP Server)</title>
		<link rel="alternate" type="text/html" href="https://interfaces3.hunter.cryptograss.live/index.php?title=Module:Src&amp;diff=5410&amp;oldid=prev"/>
		<updated>2026-08-16T22:03:52Z</updated>

		<summary type="html">&lt;p&gt;Src: inline citations that are also queryable subobjects (via create-page on MediaWiki MCP Server)&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;--[[&lt;br /&gt;
Src — inline citations that are also queryable data.&lt;br /&gt;
&lt;br /&gt;
Every {{Src}} does two jobs at once. It renders a marker a reader can follow,&lt;br /&gt;
and it emits a Semantic MediaWiki subobject describing what the citation&lt;br /&gt;
actually points at. The subobject is the reason this module exists: rendering a&lt;br /&gt;
footnote is easy, but asking &amp;quot;every claim on this wiki attested by somebody who&lt;br /&gt;
was actually on the record&amp;quot; needs structure sitting behind the marker.&lt;br /&gt;
&lt;br /&gt;
On not using the Cite extension: a named &amp;lt;ref&amp;gt; is byte-identical everywhere it&lt;br /&gt;
is reused, so it cannot express &amp;quot;this video at 14:32&amp;quot; and &amp;quot;the same video at&lt;br /&gt;
21:07&amp;quot;. Here resource-plus-offset is the ordinary case rather than an edge one,&lt;br /&gt;
which rules Cite out on mechanics, not on taste.&lt;br /&gt;
&lt;br /&gt;
Each renderer takes the argument table and returns either:&lt;br /&gt;
  a table  { link = &amp;lt;wikitext&amp;gt;, props = &amp;lt;ordered pairs&amp;gt; }  on success&lt;br /&gt;
  a string                                                  an error message&lt;br /&gt;
&lt;br /&gt;
props is an ordered list of {property, value} pairs rather than a keyed table,&lt;br /&gt;
so the emitted wikitext comes out in a stable order. That keeps page diffs&lt;br /&gt;
readable when somebody edits an unrelated line.&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
local ETHERSCAN = &amp;#039;https://etherscan.io&amp;#039;&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------&lt;br /&gt;
-- Helpers&lt;br /&gt;
--------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
--- Parse a timecode into whole seconds.&lt;br /&gt;
--&lt;br /&gt;
-- Accepts &amp;quot;14:32&amp;quot;, &amp;quot;1:02:03&amp;quot;, or a bare count of seconds.&lt;br /&gt;
--&lt;br /&gt;
-- @param raw string|nil Raw parameter value.&lt;br /&gt;
-- @return number|nil Seconds, or nil when unparseable. Unparseable degrades to&lt;br /&gt;
--   a citation without a timecode rather than an error blot in the middle of&lt;br /&gt;
--   an article — a typo should not cost the reader the whole sentence.&lt;br /&gt;
local function parseTimecode( raw )&lt;br /&gt;
	if not raw or raw == &amp;#039;&amp;#039; then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	local total = 0&lt;br /&gt;
	local parts = mw.text.split( mw.text.trim( raw ), &amp;#039;:&amp;#039;, true )&lt;br /&gt;
	for _, part in ipairs( parts ) do&lt;br /&gt;
		local n = tonumber( part )&lt;br /&gt;
		if not n or n &amp;lt; 0 then&lt;br /&gt;
			return nil&lt;br /&gt;
		end&lt;br /&gt;
		total = total * 60 + n&lt;br /&gt;
	end&lt;br /&gt;
	return math.floor( total )&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Render seconds back as a timecode: 872 becomes &amp;quot;14:32&amp;quot;, 3723 &amp;quot;1:02:03&amp;quot;.&lt;br /&gt;
--&lt;br /&gt;
-- @param seconds number&lt;br /&gt;
-- @return string&lt;br /&gt;
local function formatTimecode( seconds )&lt;br /&gt;
	local h = math.floor( seconds / 3600 )&lt;br /&gt;
	local m = math.floor( ( seconds % 3600 ) / 60 )&lt;br /&gt;
	local s = seconds % 60&lt;br /&gt;
	if h &amp;gt; 0 then&lt;br /&gt;
		return string.format( &amp;#039;%d:%02d:%02d&amp;#039;, h, m, s )&lt;br /&gt;
	end&lt;br /&gt;
	return string.format( &amp;#039;%d:%02d&amp;#039;, m, s )&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Shorten a long opaque identifier for display, keeping both ends.&lt;br /&gt;
--&lt;br /&gt;
-- Hashes are recognised by their head and tail, so an ellipsis in the middle&lt;br /&gt;
-- stays useful where a plain truncation does not.&lt;br /&gt;
--&lt;br /&gt;
-- @param value string&lt;br /&gt;
-- @param keep number How many leading characters to show.&lt;br /&gt;
-- @return string&lt;br /&gt;
local function abbreviate( value, keep )&lt;br /&gt;
	if #value &amp;lt;= keep + 5 then&lt;br /&gt;
		return value&lt;br /&gt;
	end&lt;br /&gt;
	return value:sub( 1, keep ) .. &amp;#039;…&amp;#039; .. value:sub( -4 )&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Collect template and invoke arguments into one trimmed table.&lt;br /&gt;
--&lt;br /&gt;
-- Values arriving from a template wrapper carry whitespace from the wikitext&lt;br /&gt;
-- that called it; everything downstream assumes trimmed input.&lt;br /&gt;
--&lt;br /&gt;
-- @param frame table&lt;br /&gt;
-- @return table&lt;br /&gt;
local function getArgs( frame )&lt;br /&gt;
	local args = {}&lt;br /&gt;
	local parent = frame:getParent()&lt;br /&gt;
	if parent and parent.args then&lt;br /&gt;
		for k, v in pairs( parent.args ) do&lt;br /&gt;
			args[k] = v&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	for k, v in pairs( frame.args ) do&lt;br /&gt;
		args[k] = v&lt;br /&gt;
	end&lt;br /&gt;
	for k, v in pairs( args ) do&lt;br /&gt;
		if type( v ) == &amp;#039;string&amp;#039; then&lt;br /&gt;
			args[k] = mw.text.trim( v )&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return args&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Non-empty string, or nil.&lt;br /&gt;
local function present( value )&lt;br /&gt;
	if value and value ~= &amp;#039;&amp;#039; then&lt;br /&gt;
		return value&lt;br /&gt;
	end&lt;br /&gt;
	return nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------&lt;br /&gt;
-- Renderers, one per source type&lt;br /&gt;
--------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local renderers = {}&lt;br /&gt;
&lt;br /&gt;
--- A moment in an archived video — the workhorse.&lt;br /&gt;
--&lt;br /&gt;
-- Cites the exact second at which someone asserts the thing. The #t= fragment&lt;br /&gt;
-- is read by the HLS player gadget in MediaWiki:Common.js, which seeks the&lt;br /&gt;
-- Release page&amp;#039;s player to that second on arrival, so following the citation&lt;br /&gt;
-- lands the reader on the assertion itself rather than at 0:00.&lt;br /&gt;
renderers.video = function( args )&lt;br /&gt;
	local cid = present( args.cid ) or present( args[2] )&lt;br /&gt;
	if not cid then&lt;br /&gt;
		return &amp;#039;video citation needs cid=&amp;#039;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local props = {&lt;br /&gt;
		{ &amp;#039;Has source type&amp;#039;, &amp;#039;video&amp;#039; },&lt;br /&gt;
		{ &amp;#039;Cites release&amp;#039;, &amp;#039;Release:&amp;#039; .. cid },&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	local seconds = parseTimecode( args.t )&lt;br /&gt;
	if not seconds then&lt;br /&gt;
		return {&lt;br /&gt;
			link = &amp;#039;[[Release:&amp;#039; .. cid .. &amp;#039;|▶ video]]&amp;#039;,&lt;br /&gt;
			props = props,&lt;br /&gt;
		}&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	table.insert( props, { &amp;#039;Has timecode&amp;#039;, tostring( seconds ) } )&lt;br /&gt;
	return {&lt;br /&gt;
		link = &amp;#039;[[Release:&amp;#039; .. cid .. &amp;#039;#t=&amp;#039; .. seconds .. &amp;#039;|▶ &amp;#039;&lt;br /&gt;
			.. formatTimecode( seconds ) .. &amp;#039;]]&amp;#039;,&lt;br /&gt;
		props = props,&lt;br /&gt;
	}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- A particular edit, by somebody in a position to know.&lt;br /&gt;
--&lt;br /&gt;
-- Distinct from testimony: this points at a dated, immutable revision rather&lt;br /&gt;
-- than at a person&amp;#039;s standing assertion. The revision cannot be edited out&lt;br /&gt;
-- from under the citation.&lt;br /&gt;
renderers.edit = function( args )&lt;br /&gt;
	local oldid = present( args.oldid )&lt;br /&gt;
	if not oldid or not tonumber( oldid ) then&lt;br /&gt;
		return &amp;#039;edit citation needs a numeric oldid=&amp;#039;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local props = {&lt;br /&gt;
		{ &amp;#039;Has source type&amp;#039;, &amp;#039;edit&amp;#039; },&lt;br /&gt;
		{ &amp;#039;Cites revision&amp;#039;, oldid },&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	local by = present( args.by )&lt;br /&gt;
	local label = &amp;#039;edit&amp;#039;&lt;br /&gt;
	if by then&lt;br /&gt;
		table.insert( props, { &amp;#039;Asserted by&amp;#039;, &amp;#039;User:&amp;#039; .. by } )&lt;br /&gt;
		label = &amp;#039;edit by &amp;#039; .. by&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return {&lt;br /&gt;
		link = &amp;#039;[[Special:PermanentLink/&amp;#039; .. oldid .. &amp;#039;|✎ &amp;#039; .. label .. &amp;#039;]]&amp;#039;,&lt;br /&gt;
		props = props,&lt;br /&gt;
	}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- An on-chain fact.&lt;br /&gt;
--&lt;br /&gt;
-- Alone among these types, a chain citation carries its own timestamp: it can&lt;br /&gt;
-- show that something was asserted *before* a given block, which no amount of&lt;br /&gt;
-- wiki history can establish on its own.&lt;br /&gt;
renderers.chain = function( args )&lt;br /&gt;
	local tx = present( args.tx )&lt;br /&gt;
	local block = present( args.block )&lt;br /&gt;
	if not tx and not block then&lt;br /&gt;
		return &amp;#039;chain citation needs tx= or block=&amp;#039;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local props = { { &amp;#039;Has source type&amp;#039;, &amp;#039;chain&amp;#039; } }&lt;br /&gt;
&lt;br /&gt;
	if tx then&lt;br /&gt;
		table.insert( props, { &amp;#039;Has transaction&amp;#039;, tx } )&lt;br /&gt;
		return {&lt;br /&gt;
			link = &amp;#039;[&amp;#039; .. ETHERSCAN .. &amp;#039;/tx/&amp;#039; .. tx .. &amp;#039; ⛓ &amp;#039; .. abbreviate( tx, 8 ) .. &amp;#039;]&amp;#039;,&lt;br /&gt;
			props = props,&lt;br /&gt;
		}&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	table.insert( props, { &amp;#039;At blockheight&amp;#039;, block } )&lt;br /&gt;
	return {&lt;br /&gt;
		link = &amp;#039;[&amp;#039; .. ETHERSCAN .. &amp;#039;/block/&amp;#039; .. block .. &amp;#039; ⛓ block &amp;#039; .. block .. &amp;#039;]&amp;#039;,&lt;br /&gt;
		props = props,&lt;br /&gt;
	}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- First-party assertion. &amp;quot;This is my band, and I know we play Bob&amp;#039;s Tuesday.&amp;quot;&lt;br /&gt;
--&lt;br /&gt;
-- Deliberately a first-class type rather than something to be laundered into&lt;br /&gt;
-- looking like a published source. Its weight comes from who said it, which is&lt;br /&gt;
-- exactly what the trust graph is for.&lt;br /&gt;
renderers.testimony = function( args )&lt;br /&gt;
	local by = present( args.by )&lt;br /&gt;
	if not by then&lt;br /&gt;
		return &amp;#039;testimony citation needs by=&amp;#039;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local rel = present( args.rel ) or &amp;#039;witness&amp;#039;&lt;br /&gt;
	return {&lt;br /&gt;
		link = &amp;#039;[[User:&amp;#039; .. by .. &amp;#039;|&amp;#039; .. by .. &amp;#039;, &amp;#039; .. rel .. &amp;#039;]]&amp;#039;,&lt;br /&gt;
		props = {&lt;br /&gt;
			{ &amp;#039;Has source type&amp;#039;, &amp;#039;testimony&amp;#039; },&lt;br /&gt;
			{ &amp;#039;Asserted by&amp;#039;, &amp;#039;User:&amp;#039; .. by },&lt;br /&gt;
			{ &amp;#039;Has relationship&amp;#039;, rel },&lt;br /&gt;
		},&lt;br /&gt;
	}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- A recording or release as a whole, with no particular moment singled out.&lt;br /&gt;
renderers.recording = function( args )&lt;br /&gt;
	local page = present( args.page ) or present( args[2] )&lt;br /&gt;
	if not page then&lt;br /&gt;
		return &amp;#039;recording citation needs page=&amp;#039;&lt;br /&gt;
	end&lt;br /&gt;
	return {&lt;br /&gt;
		link = &amp;#039;[[&amp;#039; .. page .. &amp;#039;|♫ &amp;#039; .. ( present( args.label ) or &amp;#039;recording&amp;#039; ) .. &amp;#039;]]&amp;#039;,&lt;br /&gt;
		props = {&lt;br /&gt;
			{ &amp;#039;Has source type&amp;#039;, &amp;#039;recording&amp;#039; },&lt;br /&gt;
			{ &amp;#039;Cites release&amp;#039;, page },&lt;br /&gt;
		},&lt;br /&gt;
	}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- An ordinary published source somewhere off-wiki.&lt;br /&gt;
renderers.published = function( args )&lt;br /&gt;
	local url = present( args.url )&lt;br /&gt;
	if not url then&lt;br /&gt;
		return &amp;#039;published citation needs url=&amp;#039;&lt;br /&gt;
	end&lt;br /&gt;
	return {&lt;br /&gt;
		link = &amp;#039;[&amp;#039; .. url .. &amp;#039; &amp;#039; .. ( present( args.title ) or &amp;#039;source&amp;#039; ) .. &amp;#039;]&amp;#039;,&lt;br /&gt;
		props = {&lt;br /&gt;
			{ &amp;#039;Has source type&amp;#039;, &amp;#039;published&amp;#039; },&lt;br /&gt;
			{ &amp;#039;Cites url&amp;#039;, url },&lt;br /&gt;
		},&lt;br /&gt;
	}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------&lt;br /&gt;
-- Entry point&lt;br /&gt;
--------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
--- Render an error in place, visibly but without killing the page.&lt;br /&gt;
local function errorSpan( message )&lt;br /&gt;
	return &amp;#039;&amp;lt;span class=&amp;quot;error pp-src-error&amp;quot;&amp;gt;[Src: &amp;#039; .. mw.text.nowiki( message ) .. &amp;#039;]&amp;lt;/span&amp;gt;&amp;#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Build the subobject wikitext for a citation.&lt;br /&gt;
--&lt;br /&gt;
-- Anonymous subobjects are fine here: nothing needs to address an individual&lt;br /&gt;
-- citation by name, and auto-generated ids keep authors from having to invent&lt;br /&gt;
-- unique keys for every footnote on a page.&lt;br /&gt;
local function subobject( frame, kind, props )&lt;br /&gt;
	local parts = {}&lt;br /&gt;
	for _, pair in ipairs( props ) do&lt;br /&gt;
		-- Only the first &amp;#039;=&amp;#039; separates name from value in a parser function&lt;br /&gt;
		-- argument, so values containing &amp;#039;=&amp;#039; (query strings, mostly) survive.&lt;br /&gt;
		-- A literal &amp;#039;|&amp;#039; would not, hence the escape.&lt;br /&gt;
		local value = tostring( pair[2] ):gsub( &amp;#039;|&amp;#039;, &amp;#039;&amp;amp;#124;&amp;#039; )&lt;br /&gt;
		table.insert( parts, &amp;#039;|&amp;#039; .. pair[1] .. &amp;#039;=&amp;#039; .. value )&lt;br /&gt;
	end&lt;br /&gt;
	table.insert( parts, &amp;#039;|@category=Citations&amp;#039; )&lt;br /&gt;
	table.insert( parts, &amp;#039;|@sortkey=&amp;#039; .. kind )&lt;br /&gt;
	return frame:preprocess( &amp;#039;{{#subobject:&amp;#039; .. table.concat( parts ) .. &amp;#039;}}&amp;#039; )&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- {{Src|&amp;lt;type&amp;gt;|...}}&lt;br /&gt;
--&lt;br /&gt;
-- @param frame table&lt;br /&gt;
-- @return string Marker wikitext, with the subobject attached invisibly.&lt;br /&gt;
function p.src( frame )&lt;br /&gt;
	local args = getArgs( frame )&lt;br /&gt;
	local kind = present( args[1] )&lt;br /&gt;
	if not kind then&lt;br /&gt;
		return errorSpan( &amp;#039;no source type given&amp;#039; )&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	kind = kind:lower()&lt;br /&gt;
	local renderer = renderers[kind]&lt;br /&gt;
	if not renderer then&lt;br /&gt;
		return errorSpan( &amp;#039;unknown source type &amp;quot;&amp;#039; .. kind .. &amp;#039;&amp;quot;&amp;#039; )&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local result = renderer( args )&lt;br /&gt;
	if type( result ) == &amp;#039;string&amp;#039; then&lt;br /&gt;
		return errorSpan( result )&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return &amp;#039;&amp;lt;sup class=&amp;quot;pp-src pp-src-&amp;#039; .. kind .. &amp;#039;&amp;quot;&amp;gt;[&amp;#039; .. result.link .. &amp;#039;]&amp;lt;/sup&amp;gt;&amp;#039;&lt;br /&gt;
		.. subobject( frame, kind, result.props )&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Magent</name></author>
	</entry>
</feed>