<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://eurovision.jobogamer.xyz/index.php?action=history&amp;feed=atom&amp;title=Module%3ADiff</id>
	<title>Module:Diff - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://eurovision.jobogamer.xyz/index.php?action=history&amp;feed=atom&amp;title=Module%3ADiff"/>
	<link rel="alternate" type="text/html" href="https://eurovision.jobogamer.xyz/index.php?title=Module:Diff&amp;action=history"/>
	<updated>2026-05-20T13:59:01Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.45.3</generator>
	<entry>
		<id>https://eurovision.jobogamer.xyz/index.php?title=Module:Diff&amp;diff=4100&amp;oldid=prev</id>
		<title>imported&gt;MusikBot II: Changed protection settings for &quot;Module:Diff&quot;: High-risk template or module: 2500 transclusions (more info) ([Edit=Require extended confirmed access] (indefinite) [Move=Require extended confirmed access] (indefinite))</title>
		<link rel="alternate" type="text/html" href="https://eurovision.jobogamer.xyz/index.php?title=Module:Diff&amp;diff=4100&amp;oldid=prev"/>
		<updated>2026-02-14T18:00:33Z</updated>

		<summary type="html">&lt;p&gt;Changed protection settings for &amp;quot;&lt;a href=&quot;/wiki/Module:Diff&quot; title=&quot;Module:Diff&quot;&gt;Module:Diff&lt;/a&gt;&amp;quot;: &lt;a href=&quot;/index.php?title=Eurovision_Wiki:High-risk_templates&amp;amp;action=edit&amp;amp;redlink=1&quot; class=&quot;new&quot; title=&quot;Eurovision Wiki:High-risk templates (page does not exist)&quot;&gt;High-risk template or module&lt;/a&gt;: 2500 transclusions (&lt;a href=&quot;/index.php?title=User:MusikBot_II/TemplateProtector&amp;amp;action=edit&amp;amp;redlink=1&quot; class=&quot;new&quot; title=&quot;User:MusikBot II/TemplateProtector (page does not exist)&quot;&gt;more info&lt;/a&gt;) ([Edit=Require extended confirmed access] (indefinite) [Move=Require extended confirmed access] (indefinite))&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;--- Provides functions for diffing text.&lt;br /&gt;
--&lt;br /&gt;
--&lt;br /&gt;
-- @module diff&lt;br /&gt;
-- @alias p&lt;br /&gt;
-- @author Yuri Takhteyev (yuri@freewisdom.org)&lt;br /&gt;
-- @author Hisham Muhammad&lt;br /&gt;
-- @author [[User:Ebrahim]]&lt;br /&gt;
-- @release stable&lt;br /&gt;
-- @license MIT/X&lt;br /&gt;
&lt;br /&gt;
-- (c) 2007, 2008  Yuri Takhteyev (yuri@freewisdom.org)&lt;br /&gt;
-- (c) 2007 Hisham Muhammad&lt;br /&gt;
-- Adapted to MediaWiki Lua originally by [[User:Ebrahim]]&lt;br /&gt;
-- License: MIT/X, see http://sputnik.freewisdom.org/en/License&lt;br /&gt;
&lt;br /&gt;
local SKIP_SEPARATOR = true  -- a constant&lt;br /&gt;
&lt;br /&gt;
-- token statuses&lt;br /&gt;
local IN   = &amp;quot;in&amp;quot;&lt;br /&gt;
local OUT  = &amp;quot;out&amp;quot;&lt;br /&gt;
local SAME = &amp;quot;same&amp;quot;&lt;br /&gt;
&lt;br /&gt;
--- Split a string into tokens.  (Adapted from Gavin Kistner&amp;#039;s split on&lt;br /&gt;
-- http://lua-users.org/wiki/SplitJoin.&lt;br /&gt;
--&lt;br /&gt;
-- @param text           A string to be split.&lt;br /&gt;
-- @param separator      [optional] the separator pattern (defaults to any&lt;br /&gt;
--                       whitespace - %s+).&lt;br /&gt;
-- @param skip_separator [optional] don&amp;#039;t include the separator in the results.&lt;br /&gt;
-- @return               A list of tokens.&lt;br /&gt;
local function split(text, separator, skip_separator)&lt;br /&gt;
	separator = separator or &amp;quot;%s+&amp;quot;&lt;br /&gt;
	local parts = {}&lt;br /&gt;
	local start = 1&lt;br /&gt;
	local split_start, split_end = mw.ustring.find(text, separator, start)&lt;br /&gt;
	while split_start do&lt;br /&gt;
		table.insert(parts, mw.ustring.sub(text, start, split_start-1))&lt;br /&gt;
		if not skip_separator then&lt;br /&gt;
			table.insert(parts, mw.ustring.sub(text, split_start, split_end))&lt;br /&gt;
		end&lt;br /&gt;
		start = split_end + 1&lt;br /&gt;
		split_start, split_end = mw.ustring.find(text, separator, start)&lt;br /&gt;
	end&lt;br /&gt;
	if mw.ustring.sub(text, start) ~= &amp;quot;&amp;quot; then&lt;br /&gt;
		table.insert(parts, mw.ustring.sub(text, start))&lt;br /&gt;
	end&lt;br /&gt;
	return parts&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--- Derives the longest common subsequence of two strings.  This is a faster&lt;br /&gt;
-- implementation than one provided by stdlib.  Submitted by Hisham Muhammad.&lt;br /&gt;
-- The algorithm was taken from:&lt;br /&gt;
-- http://en.wikibooks.org/wiki/Algorithm_implementation/Strings/Longest_common_subsequence&lt;br /&gt;
--&lt;br /&gt;
-- @param t1             the first string.&lt;br /&gt;
-- @param t2             the second string.&lt;br /&gt;
-- @return               the least common subsequence as a matrix.&lt;br /&gt;
local function quick_LCS(t1, t2)&lt;br /&gt;
	local m = #t1&lt;br /&gt;
	local n = #t2&lt;br /&gt;
&lt;br /&gt;
	-- Build matrix on demand&lt;br /&gt;
	local C = {}&lt;br /&gt;
	local setmetatable = setmetatable&lt;br /&gt;
	local mt_tbl = {&lt;br /&gt;
		__index = function(t, k)&lt;br /&gt;
			t[k] = 0&lt;br /&gt;
			return 0&lt;br /&gt;
		end&lt;br /&gt;
	}&lt;br /&gt;
	local mt_C = {&lt;br /&gt;
		__index = function(t, k)&lt;br /&gt;
			local tbl = {}&lt;br /&gt;
			setmetatable(tbl, mt_tbl)&lt;br /&gt;
			t[k] = tbl&lt;br /&gt;
			return tbl&lt;br /&gt;
		end&lt;br /&gt;
	}&lt;br /&gt;
	setmetatable(C, mt_C)&lt;br /&gt;
	local max = math.max&lt;br /&gt;
	for i = 1, m+1 do&lt;br /&gt;
		local ci1 = C[i+1]&lt;br /&gt;
		local ci = C[i]&lt;br /&gt;
		for j = 1, n+1 do&lt;br /&gt;
			if t1[i-1] == t2[j-1] then&lt;br /&gt;
				ci1[j+1] = ci[j] + 1&lt;br /&gt;
			else&lt;br /&gt;
				ci1[j+1] = max(ci1[j], ci[j+1])&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return C&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--- Formats an inline diff as HTML, with &amp;lt;ins&amp;gt; and &amp;lt;del&amp;gt; tags.&lt;br /&gt;
--&lt;br /&gt;
-- @param tokens         a table of {token, status} pairs.&lt;br /&gt;
-- @return               an HTML string.&lt;br /&gt;
local function format_as_html(tokens)&lt;br /&gt;
	local diff_buffer = &amp;quot;&amp;quot;&lt;br /&gt;
	local token, status&lt;br /&gt;
	for i, token_record in ipairs(tokens) do&lt;br /&gt;
		token = mw.text.nowiki(token_record[1])&lt;br /&gt;
		status = token_record[2]&lt;br /&gt;
		if status == &amp;quot;in&amp;quot; then&lt;br /&gt;
			diff_buffer = diff_buffer..&amp;#039;&amp;lt;ins&amp;gt;&amp;#039;..token..&amp;#039;&amp;lt;/ins&amp;gt;&amp;#039;&lt;br /&gt;
		elseif status == &amp;quot;out&amp;quot; then&lt;br /&gt;
			diff_buffer = diff_buffer..&amp;#039;&amp;lt;del&amp;gt;&amp;#039;..token..&amp;#039;&amp;lt;/del&amp;gt;&amp;#039;&lt;br /&gt;
		else&lt;br /&gt;
			diff_buffer = diff_buffer..token&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return diff_buffer&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Returns a diff of two strings as a list of pairs, where the first value&lt;br /&gt;
-- represents a token and the second the token&amp;#039;s status (&amp;quot;same&amp;quot;, &amp;quot;in&amp;quot;, &amp;quot;out&amp;quot;).&lt;br /&gt;
--&lt;br /&gt;
-- @function p.diff&lt;br /&gt;
-- @param old             The &amp;quot;old&amp;quot; text string&lt;br /&gt;
-- @param new             The &amp;quot;new&amp;quot; text string&lt;br /&gt;
-- @param separator      [optional] the separator pattern (defaults to any&lt;br /&gt;
--                       whitespace).&lt;br /&gt;
-- @return               A list of annotated tokens.&lt;br /&gt;
local function diff(old, new, separator)&lt;br /&gt;
	assert(old); assert(new)&lt;br /&gt;
	new = split(new, separator); old = split(old, separator)&lt;br /&gt;
&lt;br /&gt;
	-- First, compare the beginnings and ends of strings to remove the common&lt;br /&gt;
	-- prefix and suffix.  Chances are, there is only a small number of tokens&lt;br /&gt;
	-- in the middle that differ, in which case  we can save ourselves a lot&lt;br /&gt;
	-- in terms of LCS computation.&lt;br /&gt;
	local prefix = &amp;quot;&amp;quot; -- common text in the beginning&lt;br /&gt;
	local suffix = &amp;quot;&amp;quot; -- common text in the end&lt;br /&gt;
	while old[1] and old[1] == new[1] do&lt;br /&gt;
		local token = table.remove(old, 1)&lt;br /&gt;
		table.remove(new, 1)&lt;br /&gt;
		prefix = prefix..token&lt;br /&gt;
	end&lt;br /&gt;
	while old[#old] and old[#old] == new[#new] do&lt;br /&gt;
		local token = table.remove(old)&lt;br /&gt;
		table.remove(new)&lt;br /&gt;
		suffix = token..suffix&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Setup a table that will store the diff (an upvalue for get_diff). We&amp;#039;ll&lt;br /&gt;
	-- store it in the reverse order to allow for tail calls.  We&amp;#039;ll also keep&lt;br /&gt;
	-- in this table functions to handle different events.&lt;br /&gt;
	local rev_diff = {&lt;br /&gt;
		put  = function(self, token, type) table.insert(self, {token,type}) end,&lt;br /&gt;
		ins  = function(self, token) self:put(token, IN) end,&lt;br /&gt;
		del  = function(self, token) self:put(token, OUT) end,&lt;br /&gt;
		same = function(self, token) if token then self:put(token, SAME) end end,&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	-- Put the suffix as the first token (we are storing the diff in the&lt;br /&gt;
	-- reverse order)&lt;br /&gt;
&lt;br /&gt;
	rev_diff:same(suffix)&lt;br /&gt;
&lt;br /&gt;
	-- Define a function that will scan the LCS matrix backwards and build the&lt;br /&gt;
	-- diff output recursively.&lt;br /&gt;
	local function get_diff(C, old, new, i, j)&lt;br /&gt;
		local old_i = old[i]&lt;br /&gt;
		local new_j = new[j]&lt;br /&gt;
		if i &amp;gt;= 1 and j &amp;gt;= 1 and old_i == new_j then&lt;br /&gt;
			rev_diff:same(old_i)&lt;br /&gt;
			return get_diff(C, old, new, i-1, j-1)&lt;br /&gt;
		else&lt;br /&gt;
			local Cij1 = C[i][j-1]&lt;br /&gt;
			local Ci1j = C[i-1][j]&lt;br /&gt;
			if j &amp;gt;= 1 and (i == 0 or Cij1 &amp;gt;= Ci1j) then&lt;br /&gt;
				rev_diff:ins(new_j)&lt;br /&gt;
				return get_diff(C, old, new, i, j-1)&lt;br /&gt;
			elseif i &amp;gt;= 1 and (j == 0 or Cij1 &amp;lt; Ci1j) then&lt;br /&gt;
				rev_diff:del(old_i)&lt;br /&gt;
				return get_diff(C, old, new, i-1, j)&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	-- Then call it.&lt;br /&gt;
	get_diff(quick_LCS(old, new), old, new, #old + 1, #new + 1)&lt;br /&gt;
&lt;br /&gt;
	-- Put the prefix in at the end&lt;br /&gt;
	rev_diff:same(prefix)&lt;br /&gt;
&lt;br /&gt;
	-- Reverse the diff.&lt;br /&gt;
	local diff = {}&lt;br /&gt;
&lt;br /&gt;
	for i = #rev_diff, 1, -1 do&lt;br /&gt;
		table.insert(diff, rev_diff[i])&lt;br /&gt;
	end&lt;br /&gt;
	diff.to_html = format_as_html&lt;br /&gt;
	return diff&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Wiki diff style, currently just for a line&lt;br /&gt;
-- @function p.wikiDiff&lt;br /&gt;
-- @param old             The &amp;quot;old&amp;quot; text string&lt;br /&gt;
-- @param new             The &amp;quot;new&amp;quot; text string&lt;br /&gt;
-- @param separator      [optional] the separator pattern (defaults to any&lt;br /&gt;
--                       whitespace).&lt;br /&gt;
-- @return			     HTML formatted diff&lt;br /&gt;
local function wikiDiff(old, new, separator)&lt;br /&gt;
	local tokens = diff(old, new, separator)&lt;br /&gt;
	local root = mw.html.create(&amp;#039;&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
	local token, status&lt;br /&gt;
&lt;br /&gt;
	local plusMinusStyle = &amp;#039;width: 2%; padding: 0.25em; font-weight: bold;&amp;#039; ..&lt;br /&gt;
		&amp;#039;font-size: 1.25em; text-align: end;&amp;#039;&lt;br /&gt;
	local tdDivStyle = &amp;#039;word-wrap: break-word; direction: ltr;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
	local tdSharedStyle = &amp;#039;vertical-align:top; width: 48%; border-style: solid; border-radius: 0.33em; &amp;#039; ..&lt;br /&gt;
		&amp;#039;padding: 0.33em 0.5em; color: inherit; font-size: 1em; font-family: monospace; white-space: pre-wrap; border-width: 1px 1px 1px 4px; &amp;#039; ..&lt;br /&gt;
		&amp;#039;-webkit-border-end-width: 1px; -webkit-border-start-width: 4px; &amp;#039; ..&lt;br /&gt;
		&amp;#039;-moz-border-end-width: 1px; -moz-border-start-width: 4px;&amp;#039; -- these override default border-width for browsers that support them, needed for RTL UI on commons&lt;br /&gt;
	local insDelSharedStyle = &amp;#039;padding: 0.25em 0; font-weight: bold; text-decoration: initial;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	local tr = root:tag(&amp;#039;table&amp;#039;):addClass(&amp;#039;diff&amp;#039;):css(&amp;#039;width&amp;#039;, &amp;#039;100%&amp;#039;):tag(&amp;#039;tr&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
	tr:tag(&amp;#039;td&amp;#039;)&lt;br /&gt;
		:addClass(&amp;#039;diff-marker&amp;#039;)&lt;br /&gt;
		:cssText(plusMinusStyle)&lt;br /&gt;
		:wikitext(&amp;#039;−&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
	local deleted = tr&lt;br /&gt;
		:tag(&amp;#039;td&amp;#039;)&lt;br /&gt;
			:cssText(&amp;#039;border-color: var(--background-color-content-removed,#ffe49c); &amp;#039; .. tdSharedStyle)&lt;br /&gt;
			:addClass(&amp;#039;diff-deletedline&amp;#039;)&lt;br /&gt;
			:tag(&amp;#039;div&amp;#039;)&lt;br /&gt;
				:cssText(tdDivStyle)&lt;br /&gt;
&lt;br /&gt;
	for i, token_record in ipairs(tokens) do&lt;br /&gt;
		token = mw.text.nowiki(token_record[1]):gsub(&amp;quot;\n&amp;quot;, &amp;quot;&amp;amp;#10;&amp;quot;) -- Force all newlines to encode to avoid linter issues&lt;br /&gt;
		status = token_record[2]&lt;br /&gt;
		if status == OUT then&lt;br /&gt;
			deleted&lt;br /&gt;
				:tag(&amp;#039;del&amp;#039;)&lt;br /&gt;
					:cssText(&amp;#039;background: var(--background-color-content-removed,#ffe49c); color: inherit; &amp;#039; .. insDelSharedStyle)&lt;br /&gt;
					:addClass(&amp;#039;diffchange&amp;#039;)&lt;br /&gt;
					:addClass(&amp;#039;diffchange-inline&amp;#039;)&lt;br /&gt;
					:wikitext(token)&lt;br /&gt;
		elseif status == SAME then&lt;br /&gt;
			deleted:wikitext(token)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	tr:tag(&amp;#039;td&amp;#039;)&lt;br /&gt;
		:cssText(plusMinusStyle)&lt;br /&gt;
		:wikitext(&amp;#039;+&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
	local inserted = tr&lt;br /&gt;
		:tag(&amp;#039;td&amp;#039;)&lt;br /&gt;
			:cssText(&amp;#039;border-color: var(--background-color-content-added,#a3d3ff); &amp;#039; .. tdSharedStyle)&lt;br /&gt;
			:addClass(&amp;#039;diff-addedline&amp;#039;)&lt;br /&gt;
			:tag(&amp;#039;div&amp;#039;)&lt;br /&gt;
				:cssText(tdDivStyle)&lt;br /&gt;
&lt;br /&gt;
	for i, token_record in ipairs(tokens) do&lt;br /&gt;
		token = mw.text.nowiki(token_record[1]):gsub(&amp;quot;\n&amp;quot;, &amp;quot;&amp;amp;#10;&amp;quot;) -- Force all newlines to encode to avoid linter issues&lt;br /&gt;
		status = token_record[2]&lt;br /&gt;
		if status == IN then&lt;br /&gt;
			inserted&lt;br /&gt;
				:tag(&amp;#039;ins&amp;#039;)&lt;br /&gt;
					:cssText(&amp;#039;background: var(--background-color-content-added,#a3d3ff); color: inherit; &amp;#039; .. insDelSharedStyle)&lt;br /&gt;
					:addClass(&amp;#039;diffchange&amp;#039;)&lt;br /&gt;
					:addClass(&amp;#039;diffchange-inline&amp;#039;)&lt;br /&gt;
					:wikitext(token)&lt;br /&gt;
		elseif status == SAME then&lt;br /&gt;
			inserted:wikitext(token)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return tostring(root)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Main entry point.&lt;br /&gt;
-- @function p.main&lt;br /&gt;
-- @param {table} frame Calling frame.&lt;br /&gt;
-- @return wikitext output&lt;br /&gt;
local function main(frame)&lt;br /&gt;
	return wikiDiff(mw.text.decode(mw.text.unstrip(frame.args[1])), mw.text.decode(mw.text.unstrip(frame.args[2])), frame.args[3] or &amp;#039;[%s%.:-]+&amp;#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Exports&lt;br /&gt;
return {&lt;br /&gt;
	diff = diff,&lt;br /&gt;
	wikiDiff = wikiDiff,&lt;br /&gt;
	main = main&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>imported&gt;MusikBot II</name></author>
	</entry>
</feed>