<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://kaboom.wiki/index.php?action=history&amp;feed=atom&amp;title=Module%3AWikitext_Parsing</id>
	<title>Module:Wikitext Parsing - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://kaboom.wiki/index.php?action=history&amp;feed=atom&amp;title=Module%3AWikitext_Parsing"/>
	<link rel="alternate" type="text/html" href="https://kaboom.wiki/index.php?title=Module:Wikitext_Parsing&amp;action=history"/>
	<updated>2026-08-03T21:26:57Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.43.1</generator>
	<entry>
		<id>https://kaboom.wiki/index.php?title=Module:Wikitext_Parsing&amp;diff=4064&amp;oldid=prev</id>
		<title>Amy: 1 revision imported: Import data from English Wikipedia (CC BY-SA 4.0)</title>
		<link rel="alternate" type="text/html" href="https://kaboom.wiki/index.php?title=Module:Wikitext_Parsing&amp;diff=4064&amp;oldid=prev"/>
		<updated>2026-08-03T02:59:31Z</updated>

		<summary type="html">&lt;p&gt;1 revision imported: Import data from English Wikipedia (CC BY-SA 4.0)&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 02:59, 3 August 2026&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;4&quot; class=&quot;diff-notice&quot; lang=&quot;en&quot;&gt;&lt;div class=&quot;mw-diff-empty&quot;&gt;(No difference)&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;!-- diff cache key kaboomwikia:diff:1.41:old-4063:rev-4064 --&gt;
&lt;/table&gt;</summary>
		<author><name>Amy</name></author>
	</entry>
	<entry>
		<id>https://kaboom.wiki/index.php?title=Module:Wikitext_Parsing&amp;diff=4063&amp;oldid=prev</id>
		<title>wikipedia&gt;Pppery: Copy from sandbox per request</title>
		<link rel="alternate" type="text/html" href="https://kaboom.wiki/index.php?title=Module:Wikitext_Parsing&amp;diff=4063&amp;oldid=prev"/>
		<updated>2026-07-29T21:33:46Z</updated>

		<summary type="html">&lt;p&gt;Copy from sandbox per request&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;require(&amp;quot;strict&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
-- We&amp;#039;re calling up to thousands of string methods each time we run, so&lt;br /&gt;
-- do this very simple micro optimisation to help a tiny bit&lt;br /&gt;
local string = string&lt;br /&gt;
&lt;br /&gt;
-- Helper functions for PrepareText --&lt;br /&gt;
local function endswith(text, subtext)&lt;br /&gt;
	return string.sub(text, -#subtext, -1) == subtext&lt;br /&gt;
end&lt;br /&gt;
local function allcases(s)&lt;br /&gt;
	return string.gsub(s, &amp;quot;%a&amp;quot;, function(c)&lt;br /&gt;
		return &amp;quot;[&amp;quot;..string.upper(c)..string.lower(c)..&amp;quot;]&amp;quot;&lt;br /&gt;
	end)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--[=[ Implementation notes&lt;br /&gt;
---- NORMAL HTML TAGS ----&lt;br /&gt;
Tags are very strict on how they want to start, but loose on how they end.&lt;br /&gt;
The start must strictly follow &amp;lt;[tAgNaMe](%s|&amp;gt;) with no room for whitespace in&lt;br /&gt;
the tag&amp;#039;s name, but may then flow as they want afterwards, making&lt;br /&gt;
&amp;lt;div\nclass\n=\n&amp;quot;\nerror\n&amp;quot;\n&amp;gt; valid. If a tag has no end, it will consume all&lt;br /&gt;
text instead of not processing.&lt;br /&gt;
&lt;br /&gt;
There&amp;#039;s no sense of escaping &amp;lt; or &amp;gt;&lt;br /&gt;
E.g.&lt;br /&gt;
 &amp;lt;div class=&amp;quot;error\&amp;gt;&amp;quot;&amp;gt; will end at \&amp;gt; despite it being inside a quote&lt;br /&gt;
 &amp;lt;div class=&amp;quot;&amp;lt;span class=&amp;quot;error&amp;quot;&amp;gt;error&amp;lt;/span&amp;gt;&amp;quot;&amp;gt; will not process the larger div&lt;br /&gt;
&lt;br /&gt;
---- NOPROCESSING TAGS (nowiki, pre, syntaxhighlight, source, etc.) ----&lt;br /&gt;
(Note: &amp;lt;source&amp;gt; is the deprecated version of &amp;lt;syntaxhighlight&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
No-processing tags have some differences to the above rules. Specifically, their&lt;br /&gt;
syntax is a lot stricter. While an opening tag follows the same set of rules, A&lt;br /&gt;
closing tag can&amp;#039;t have any sort of extra formatting. &amp;lt;/div a/a&amp;gt; is valid,&lt;br /&gt;
&amp;lt;/nowiki a/a&amp;gt; is not. Only newlines and spaces/tabs are allowed in closing tags.&lt;br /&gt;
Note that, even though tags may cause a visual change without an ending tag like&lt;br /&gt;
&amp;lt;pre&amp;gt;, one is required for the no-processing effects.&lt;br /&gt;
&lt;br /&gt;
Both the content inside the tag pair and the text in the tags will not be&lt;br /&gt;
processed. E.g. &amp;lt;nowiki |}}&amp;gt;|}}&amp;lt;/nowiki&amp;gt; would have both of the |}} escaped.&lt;br /&gt;
&lt;br /&gt;
Since we only care about these no-processing tags, we can ignore the idea of an&lt;br /&gt;
intercepting tag messing us up, and just go for the first ending we can find. If&lt;br /&gt;
there is no ending, the tag will NOT consume the rest of the text. Even if there&lt;br /&gt;
is no ending tag, the content inside the opening tag will still be unprocessed,&lt;br /&gt;
meaning {{X20|&amp;lt;nowiki }}&amp;gt;}} wouldn&amp;#039;t end at the first }} despite there being no&lt;br /&gt;
ending tag.&lt;br /&gt;
&lt;br /&gt;
There are some tags, like &amp;lt;math&amp;gt;, which also function like &amp;lt;nowiki&amp;gt; for our&lt;br /&gt;
purposes, and are handled here. Some other tags, like &amp;lt;ref&amp;gt;, have more complex&lt;br /&gt;
behaviour that can&amp;#039;t be reasonably implemented in this, and so are ignored. I&lt;br /&gt;
suspect that every tag listed in [[Special:Version]] may behave somewhat like&lt;br /&gt;
this, but that&amp;#039;s far too many cases worth checking for rarely used tags that may&lt;br /&gt;
not even have a good reason to contain {{ or }} anyways, so we leave them alone.&lt;br /&gt;
&lt;br /&gt;
---- INCLUDEONLY ----&lt;br /&gt;
While includeonly tags do technically serve the same purpose as a nowiki tag for&lt;br /&gt;
what this module does, contextually they don&amp;#039;t always make sense to escape, and&lt;br /&gt;
as such are ignored by this module entirely.&lt;br /&gt;
--]=]&lt;br /&gt;
-- This function expects the string to start with the tag&lt;br /&gt;
local validtags = {nowiki=1, pre=1, syntaxhighlight=1, source=1, math=1}&lt;br /&gt;
local function TestForNowikiTag(text, scanPosition)&lt;br /&gt;
	local tagName = string.match(text, &amp;quot;^&amp;lt;([^\n /&amp;gt;]+)&amp;quot;, scanPosition)&lt;br /&gt;
	if not tagName or not validtags[string.lower(tagName)] then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	local nextOpener = string.find(text, &amp;quot;&amp;lt;&amp;quot;, scanPosition+1, true)&lt;br /&gt;
	local nextCloser = string.find(text, &amp;quot;&amp;gt;&amp;quot;, scanPosition+1, true)&lt;br /&gt;
	if nextCloser and (not nextOpener or nextCloser &amp;lt; nextOpener) then&lt;br /&gt;
		local startingTag = string.sub(text, scanPosition, nextCloser)&lt;br /&gt;
		-- We have our starting tag (E.g. &amp;#039;&amp;lt;pre style=&amp;quot;color:red&amp;quot;&amp;gt;&amp;#039;)&lt;br /&gt;
		-- Now find our ending...&lt;br /&gt;
		if endswith(startingTag, &amp;quot;/&amp;gt;&amp;quot;) then -- self-closing tag (we are our own ending)&lt;br /&gt;
			return {&lt;br /&gt;
				Tag = tagName,&lt;br /&gt;
				Start = startingTag,&lt;br /&gt;
				Content = &amp;quot;&amp;quot;, End = &amp;quot;&amp;quot;,&lt;br /&gt;
				Length = #startingTag&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
		else&lt;br /&gt;
			local endingTagStart, endingTagEnd = string.find(text, &amp;quot;&amp;lt;/&amp;quot;..allcases(tagName)..&amp;quot;[ \t\n]*&amp;gt;&amp;quot;, scanPosition)&lt;br /&gt;
			if endingTagStart then --Regular tag formation&lt;br /&gt;
				local endingTag = string.sub(text, endingTagStart, endingTagEnd)&lt;br /&gt;
				local tagContent = string.sub(text, nextCloser+1, endingTagStart-1)&lt;br /&gt;
				return {&lt;br /&gt;
					Tag = tagName,&lt;br /&gt;
					Start = startingTag,&lt;br /&gt;
					Content = tagContent,&lt;br /&gt;
					End = endingTag,&lt;br /&gt;
					Length = #startingTag + #tagContent + #endingTag&lt;br /&gt;
				}&lt;br /&gt;
&lt;br /&gt;
			else -- Content inside still needs escaping (also linter error!)&lt;br /&gt;
				return {&lt;br /&gt;
					Tag = tagName,&lt;br /&gt;
					Start = startingTag,&lt;br /&gt;
					Content = &amp;quot;&amp;quot;, End = &amp;quot;&amp;quot;,&lt;br /&gt;
					Length = #startingTag&lt;br /&gt;
				}&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return nil&lt;br /&gt;
end&lt;br /&gt;
--[=[ Implementation Notes&lt;br /&gt;
HTML Comments are about as basic as it gets. Start at &amp;lt;!--, end at --&amp;gt;, no extra&lt;br /&gt;
conditions. If a comment has no end, it will eat all the text ahead.&lt;br /&gt;
--]=]&lt;br /&gt;
local function TestForComment(text, scanPosition)&lt;br /&gt;
	if string.match(text, &amp;quot;^&amp;lt;!%-%-&amp;quot;, scanPosition) then&lt;br /&gt;
		local commentEnd = string.find(text, &amp;quot;--&amp;gt;&amp;quot;, scanPosition+4, true)&lt;br /&gt;
		if commentEnd then&lt;br /&gt;
			return {&lt;br /&gt;
				Start = &amp;quot;&amp;lt;!--&amp;quot;, End = &amp;quot;--&amp;gt;&amp;quot;,&lt;br /&gt;
				Content = string.sub(text, scanPosition+4, commentEnd-1),&lt;br /&gt;
				Length = commentEnd-scanPosition+3&lt;br /&gt;
			}&lt;br /&gt;
		else -- Consumes all text if not given an ending&lt;br /&gt;
			return {&lt;br /&gt;
				Start = &amp;quot;&amp;lt;!--&amp;quot;, End = &amp;quot;&amp;quot;,&lt;br /&gt;
				Content = string.sub(text, scanPosition+4),&lt;br /&gt;
				Length = #text-scanPosition+1&lt;br /&gt;
			}&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--[[ Implementation notes&lt;br /&gt;
The goal of this function is to escape all text that wouldn&amp;#039;t be parsed if it&lt;br /&gt;
was preprocessed (see above implementation notes).&lt;br /&gt;
&lt;br /&gt;
Using keepComments will keep all HTML comments instead of removing them. They&lt;br /&gt;
will still be escaped regardless.&lt;br /&gt;
--]]&lt;br /&gt;
local function PrepareText(text, keepComments)&lt;br /&gt;
	local newtext = {}&lt;br /&gt;
	local scanPosition = 1&lt;br /&gt;
	local textPosition = 1&lt;br /&gt;
	while true do&lt;br /&gt;
		local nextCheck = string.find(text, &amp;quot;&amp;lt;&amp;quot;, scanPosition, true) -- Advance to the next potential tag we care about&lt;br /&gt;
		if not nextCheck then -- Done&lt;br /&gt;
			newtext[#newtext+1] = string.sub(text, textPosition)&lt;br /&gt;
			break&lt;br /&gt;
		end&lt;br /&gt;
		scanPosition = nextCheck&lt;br /&gt;
		local Comment = TestForComment(text, scanPosition)&lt;br /&gt;
		if Comment then&lt;br /&gt;
			newtext[#newtext+1] = string.sub(text, textPosition, nextCheck-1)&lt;br /&gt;
			if keepComments then&lt;br /&gt;
				newtext[#newtext+1] = Comment.Start .. mw.text.nowiki(Comment.Content) .. Comment.End&lt;br /&gt;
			end&lt;br /&gt;
			scanPosition = scanPosition + Comment.Length&lt;br /&gt;
			textPosition = scanPosition&lt;br /&gt;
		else&lt;br /&gt;
			local Tag = TestForNowikiTag(text, scanPosition)&lt;br /&gt;
			if Tag then&lt;br /&gt;
				newtext[#newtext+1] = string.sub(text, textPosition, nextCheck-1)&lt;br /&gt;
				local newTagStart = &amp;quot;&amp;lt;&amp;quot; .. mw.text.nowiki(string.sub(Tag.Start, 2, -2)) .. &amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
				local newTagEnd =&lt;br /&gt;
					Tag.End == &amp;quot;&amp;quot; and &amp;quot;&amp;quot; or -- Respect no tag ending&lt;br /&gt;
					&amp;quot;&amp;lt;/&amp;quot; .. mw.text.nowiki(string.sub(Tag.End, 3, -2)) .. &amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
				local newContent = mw.text.nowiki(Tag.Content)&lt;br /&gt;
				newtext[#newtext+1] = newTagStart .. newContent .. newTagEnd&lt;br /&gt;
				scanPosition = scanPosition + Tag.Length&lt;br /&gt;
				textPosition = scanPosition&lt;br /&gt;
			else -- Nothing special, move on...&lt;br /&gt;
				scanPosition = scanPosition + 1&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return table.concat(newtext, &amp;quot;&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
--Main entry points&lt;br /&gt;
p.PrepareText = PrepareText&lt;br /&gt;
--Extra entry points, not really required&lt;br /&gt;
p.TestForNowikiTag = TestForNowikiTag&lt;br /&gt;
p.TestForComment = TestForComment&lt;br /&gt;
&lt;br /&gt;
return p&lt;br /&gt;
&lt;br /&gt;
--[==[ console tests&lt;br /&gt;
&lt;br /&gt;
local s = [=[Hey!{{Text|&amp;lt;nowiki | ||&amp;gt;&lt;br /&gt;
Hey! }}&lt;br /&gt;
A&amp;lt;/nowiki&amp;gt;|&amp;lt;!--AAAAA|AAA--&amp;gt;Should see|Shouldn&amp;#039;t see}}]=]&lt;br /&gt;
local out = p.PrepareText(s)&lt;br /&gt;
mw.logObject(out)&lt;br /&gt;
&lt;br /&gt;
local s = [=[B&amp;lt;!--&lt;br /&gt;
Hey!&lt;br /&gt;
--&amp;gt;A]=]&lt;br /&gt;
local out = p.TestForComment(s, 2)&lt;br /&gt;
mw.logObject(out); mw.log(string.sub(s, 2, out.Length))&lt;br /&gt;
&lt;br /&gt;
]==]&lt;/div&gt;</summary>
		<author><name>wikipedia&gt;Pppery</name></author>
	</entry>
</feed>