<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.jcraft-eoe.com/index.php?action=history&amp;feed=atom&amp;title=Module%3AInventory_slot</id>
	<title>Module:Inventory slot - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.jcraft-eoe.com/index.php?action=history&amp;feed=atom&amp;title=Module%3AInventory_slot"/>
	<link rel="alternate" type="text/html" href="https://wiki.jcraft-eoe.com/index.php?title=Module:Inventory_slot&amp;action=history"/>
	<updated>2026-05-21T02:28:51Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.45.3</generator>
	<entry>
		<id>https://wiki.jcraft-eoe.com/index.php?title=Module:Inventory_slot&amp;diff=199&amp;oldid=prev</id>
		<title>Ayutac: boldly copied from minecraft wiki</title>
		<link rel="alternate" type="text/html" href="https://wiki.jcraft-eoe.com/index.php?title=Module:Inventory_slot&amp;diff=199&amp;oldid=prev"/>
		<updated>2026-04-25T04:35:37Z</updated>

		<summary type="html">&lt;p&gt;boldly copied from minecraft wiki&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;local p = {}&lt;br /&gt;
&lt;br /&gt;
-- Internationalization data&lt;br /&gt;
local i18n = {&lt;br /&gt;
	-- Name formats for pages and files&lt;br /&gt;
	filename = &amp;#039;Invicon $1&amp;#039;,&lt;br /&gt;
	legacyFilename = &amp;#039;Grid $1&amp;#039;,&lt;br /&gt;
	modLink = &amp;#039;Mods/$1/$2&amp;#039;,&lt;br /&gt;
	&lt;br /&gt;
	-- Dependencies&lt;br /&gt;
	moduleAliases = [[Module:Inventory slot/Aliases]],&lt;br /&gt;
	moduleRandom = [[Module:Random]],&lt;br /&gt;
	&lt;br /&gt;
	-- List of special prefixes which should be handled by&lt;br /&gt;
	-- other modules (such as being moved outside links)&lt;br /&gt;
	-- When localizing, you might want to use a separate list of patterns&lt;br /&gt;
	-- matching the prefixes’ grammatical forms depending on the language&lt;br /&gt;
	prefixes = {&lt;br /&gt;
		any = &amp;#039;Any&amp;#039;,&lt;br /&gt;
		matching = &amp;#039;Matching&amp;#039;,&lt;br /&gt;
		damaged = &amp;#039;Damaged&amp;#039;,&lt;br /&gt;
		unwaxed = &amp;#039;Unwaxed&amp;#039;,&lt;br /&gt;
	},&lt;br /&gt;
	&lt;br /&gt;
	-- List of suffixes that are usually stripped from links and tooltips&lt;br /&gt;
	suffixes = {&lt;br /&gt;
		rev = &amp;#039;Revision %d+&amp;#039;,&lt;br /&gt;
		-- berev = &amp;#039;BE%d+&amp;#039;,&lt;br /&gt;
		-- jerev= &amp;#039;JE%d+&amp;#039;,&lt;br /&gt;
		be = &amp;#039;BE&amp;#039;,&lt;br /&gt;
		lce = &amp;#039;LCE&amp;#039;,&lt;br /&gt;
		sm = &amp;#039;SM&amp;#039;,&lt;br /&gt;
		damage = &amp;#039;%d+&amp;#039;&lt;br /&gt;
	},&lt;br /&gt;
}&lt;br /&gt;
p.i18n = i18n&lt;br /&gt;
&lt;br /&gt;
-- Global dependencies and constants&lt;br /&gt;
local random = require( i18n.moduleRandom ).random&lt;br /&gt;
local aliases = mw.loadData( i18n.moduleAliases )&lt;br /&gt;
local pageName = mw.title.getCurrentTitle().text&lt;br /&gt;
local vanilla = { v = 1, vanilla = 1, mc = 1, minecraft = 1 }&lt;br /&gt;
&lt;br /&gt;
-- Auxilliary functions --&lt;br /&gt;
&lt;br /&gt;
-- Splits a given text into fragments separated by semicolons that are not&lt;br /&gt;
-- inside square brackets. Originally written by AttemptToCallNil for the&lt;br /&gt;
-- Russian wiki.&lt;br /&gt;
-- It processes the text byte-by-byte due to being written under a much stricter&lt;br /&gt;
-- Lua runtime budget, with no LuaSandbox and mw.text.split being unperformant.&lt;br /&gt;
-- See also https://help.fandom.com/wiki/Extension:Scribunto#Known_issues_and_solutions&lt;br /&gt;
local function splitOnUnenclosedSemicolons(text)&lt;br /&gt;
	local semicolon, lbrace, rbrace = (&amp;quot;;[]&amp;quot;):byte(1, 3)&lt;br /&gt;
	local nesting = false&lt;br /&gt;
	local splitStart = 1&lt;br /&gt;
	local frameIndex = 1&lt;br /&gt;
	local frames = {}&lt;br /&gt;
	&lt;br /&gt;
	for index = 1, text:len() do&lt;br /&gt;
		local byte = text:byte(index)&lt;br /&gt;
		if byte == semicolon and not nesting then&lt;br /&gt;
			frames[frameIndex] = text:sub(splitStart, index - 1)&lt;br /&gt;
			frameIndex = frameIndex + 1&lt;br /&gt;
			splitStart = index + 1&lt;br /&gt;
		elseif byte == lbrace then&lt;br /&gt;
			assert(not nesting, &amp;quot;Excessive square brackets found&amp;quot;)&lt;br /&gt;
			nesting = true&lt;br /&gt;
		elseif byte == rbrace then&lt;br /&gt;
			assert(nesting, &amp;quot;Unbalanced square brackets found&amp;quot;)&lt;br /&gt;
			nesting = false&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	assert(not nesting, &amp;quot;Unbalanced square brackets found&amp;quot;)&lt;br /&gt;
	frames[frameIndex] = text:sub(splitStart, text:len())&lt;br /&gt;
	&lt;br /&gt;
	for index = 1, #frames do&lt;br /&gt;
		frames[index] = (frames[index]:gsub(&amp;quot;^%s+&amp;quot;, &amp;quot;&amp;quot;):gsub(&amp;quot;%s+$&amp;quot;, &amp;quot;&amp;quot;)) -- faster than mw.text.trim&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return frames&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Performs a simple recursive clone of a table’s values.&lt;br /&gt;
-- Probably exists due to mw.clone() being unusable on tables from mw.loadData()&lt;br /&gt;
-- at the time (see the link to help.fandom.com above)&lt;br /&gt;
local function cloneTable( origTable )&lt;br /&gt;
	local newTable = {}&lt;br /&gt;
	for k, v in pairs( origTable ) do&lt;br /&gt;
		if type( v ) == &amp;#039;table&amp;#039; then&lt;br /&gt;
			v = cloneTable( v )&lt;br /&gt;
		end&lt;br /&gt;
		newTable[k] = v&lt;br /&gt;
	end&lt;br /&gt;
	return newTable&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Merges a list, or inserts a string or table into a table,&lt;br /&gt;
-- depending on what the second argument happens to be&lt;br /&gt;
local function mergeList( parentTable, content )&lt;br /&gt;
	local i = #parentTable + 1&lt;br /&gt;
	if content[1] then&lt;br /&gt;
		-- Merge list into table&lt;br /&gt;
		for _, v in ipairs( content ) do&lt;br /&gt;
			parentTable[i] = v&lt;br /&gt;
			i = i + 1&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		-- Add strings or tables to table&lt;br /&gt;
		parentTable[i] = content&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Creates the HTML node for a given item.&lt;br /&gt;
-- The actual icon file is found and added here&lt;br /&gt;
local function makeItem( frame, args )&lt;br /&gt;
	local item = ( mw.html.create(&amp;#039;span&amp;#039;)&lt;br /&gt;
		:addClass(&amp;#039;invslot-item&amp;#039;)&lt;br /&gt;
		:addClass(args.imgclass)&lt;br /&gt;
		:cssText(args.imgstyle)&lt;br /&gt;
	)&lt;br /&gt;
	&lt;br /&gt;
	if (frame.name or &amp;#039;&amp;#039;) == &amp;#039;&amp;#039; then&lt;br /&gt;
		-- Empty frame, no icon to add&lt;br /&gt;
		return item&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Frame parameters&lt;br /&gt;
	local title = frame.title or mw.text.trim( args.title or &amp;#039;&amp;#039; )&lt;br /&gt;
	local mod = frame.mod&lt;br /&gt;
	local name = frame.name&lt;br /&gt;
	local num = frame.num&lt;br /&gt;
	local description = frame.text&lt;br /&gt;
	&lt;br /&gt;
	-- Split the extension out of the frame’s name&lt;br /&gt;
	local extension&lt;br /&gt;
	if name:match(&amp;#039;%.gif&amp;#039;) or name:match(&amp;#039;%.png&amp;#039;) then&lt;br /&gt;
		extension = name:sub(-4)&lt;br /&gt;
		name = name:sub(0, -5)&lt;br /&gt;
	elseif name:match(&amp;#039;%.webp&amp;#039;) then&lt;br /&gt;
		extension = &amp;#039;.webp&amp;#039;&lt;br /&gt;
		name = name:sub(0, -6)&lt;br /&gt;
	else&lt;br /&gt;
		extension = &amp;#039;.png&amp;#039;&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Determine the file name&lt;br /&gt;
	local img&lt;br /&gt;
	if mod then&lt;br /&gt;
		-- Legacy mod support&lt;br /&gt;
		-- Comment out instead of deleting, as other wikis may find it useful&lt;br /&gt;
		img = i18n.legacyFilename:gsub( &amp;#039;%$1&amp;#039;, name .. &amp;#039; (&amp;#039; .. mod .. &amp;#039;)&amp;#039; )&lt;br /&gt;
	else&lt;br /&gt;
		-- Fall back to an individual image if the sprite is lacking&lt;br /&gt;
		img = i18n.filename:gsub( &amp;#039;%$1&amp;#039;, name)&lt;br /&gt;
	end&lt;br /&gt;
	img = img .. extension&lt;br /&gt;
&lt;br /&gt;
	-- Strip suffixes out&lt;br /&gt;
	for _, suffix in pairs( i18n.suffixes ) do&lt;br /&gt;
		name = name:gsub( &amp;#039; &amp;#039; .. suffix .. &amp;#039;$&amp;#039;, &amp;#039;&amp;#039; )&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Determine the link’s target&lt;br /&gt;
	local link = args.link or &amp;#039;&amp;#039;&lt;br /&gt;
	if link == &amp;#039;&amp;#039; then&lt;br /&gt;
		if mod then&lt;br /&gt;
			link = i18n.modLink:gsub( &amp;#039;%$1&amp;#039;, mod ):gsub( &amp;#039;%$2&amp;#039;, name )&lt;br /&gt;
		else&lt;br /&gt;
			-- Strip the “Damaged” prefix out&lt;br /&gt;
			link = name:gsub( &amp;#039;^&amp;#039; .. i18n.prefixes.damaged .. &amp;#039; &amp;#039;, &amp;#039;&amp;#039; )&lt;br /&gt;
		end&lt;br /&gt;
	elseif link:lower() == &amp;#039;none&amp;#039; then&lt;br /&gt;
		-- Disable the link&lt;br /&gt;
		link = nil&lt;br /&gt;
	end&lt;br /&gt;
	if link and link:gsub(&amp;#039;^%l&amp;#039;, string.upper) == pageName then&lt;br /&gt;
		link = nil&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Tooltip titles. If JavaScript is not enabled, the slot will gracefully&lt;br /&gt;
	-- degrade to a simplified title without minetip formatting&lt;br /&gt;
	local formattedTitle&lt;br /&gt;
	local plainTitle&lt;br /&gt;
	if title == &amp;#039;&amp;#039; then&lt;br /&gt;
		-- If the title is not set, default to the slot’s name&lt;br /&gt;
		plainTitle = name&lt;br /&gt;
	elseif title:lower() ~= &amp;#039;none&amp;#039; then&lt;br /&gt;
		-- Special character escapes&lt;br /&gt;
		plainTitle = title:gsub( &amp;#039;\\\\&amp;#039;, &amp;#039;&amp;amp;#92;&amp;#039; ):gsub( &amp;#039;\\&amp;amp;&amp;#039;, &amp;#039;&amp;amp;#38;&amp;#039; )&lt;br /&gt;
		&lt;br /&gt;
		-- The default title will have special formatting code stripped out&lt;br /&gt;
		local formatPatterns = {&amp;#039;&amp;amp;[0-9a-jl-qs-vyzr]&amp;#039;, &amp;#039;&amp;amp;#%x%x%x%x%x%x&amp;#039;, &amp;#039;&amp;amp;$%x%x%x&amp;#039;}&lt;br /&gt;
		for _, formatPattern in ipairs( formatPatterns ) do&lt;br /&gt;
			if plainTitle:match( formatPattern ) then&lt;br /&gt;
				formattedTitle = title&lt;br /&gt;
				plainTitle = plainTitle:gsub( formatPattern, &amp;#039;&amp;#039; )&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		if plainTitle == &amp;#039;&amp;#039; then&lt;br /&gt;
			-- If the title field only has formatting code, the frame’s name&lt;br /&gt;
			-- is automatically used. For minetips it’s done by JavaScript&lt;br /&gt;
			-- by appending the plain title.&lt;br /&gt;
			plainTitle = name&lt;br /&gt;
		else&lt;br /&gt;
			-- Re-encode the &lt;br /&gt;
			plainTitle = plainTitle:gsub( &amp;#039;&amp;amp;#92;&amp;#039;, &amp;#039;\\&amp;#039; ):gsub( &amp;#039;&amp;amp;#38;&amp;#039;, &amp;#039;&amp;amp;&amp;#039; )&lt;br /&gt;
		end&lt;br /&gt;
	elseif link then&lt;br /&gt;
		-- Disable the tooltip that will otherwise appear with a link&lt;br /&gt;
		formattedTitle = &amp;#039;&amp;#039;&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Minetips are controlled by custom HTML attributes.&lt;br /&gt;
	-- See [[MediaWiki:Common.js]] for implementation in JavaScript&lt;br /&gt;
	item:attr{&lt;br /&gt;
		[&amp;#039;data-minetip-title&amp;#039;] = formattedTitle,&lt;br /&gt;
		[&amp;#039;data-minetip-text&amp;#039;] = description&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	-- &amp;amp; is re-escaped because mw.html treats attributes as plain text,&lt;br /&gt;
	-- but MediaWiki doesn’t.&lt;br /&gt;
	local escapedTitle = ( plainTitle or &amp;#039;&amp;#039; ):gsub( &amp;#039;&amp;amp;&amp;#039;, &amp;#039;&amp;amp;#38;&amp;#039; )&lt;br /&gt;
	&lt;br /&gt;
	-- Alt text&lt;br /&gt;
	local altText = img .. &amp;#039;: Inventory sprite for &amp;#039; .. name .. &amp;#039; in Minecraft as shown in-game&amp;#039;&lt;br /&gt;
	if link then&lt;br /&gt;
		altText = altText .. &amp;#039; linking to &amp;#039; .. link&lt;br /&gt;
	end&lt;br /&gt;
	if formattedTitle or plainTitle or link then&lt;br /&gt;
		altText = altText .. &amp;#039; with description: &amp;#039; .. ( formattedTitle or plainTitle or link )&lt;br /&gt;
		if description then&lt;br /&gt;
			altText = altText .. &amp;#039; &amp;#039; .. description:gsub( &amp;#039;/&amp;#039;, &amp;#039; &amp;#039; )&lt;br /&gt;
		end&lt;br /&gt;
		altText = altText:gsub( &amp;#039;&amp;amp;[0-9a-jl-qs-wr]&amp;#039;, &amp;#039;&amp;#039; )&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Add the image&lt;br /&gt;
	item:addClass( &amp;#039;invslot-item-image&amp;#039; )&lt;br /&gt;
		:wikitext( &amp;#039;[[File:&amp;#039;, img, &amp;#039;|32x32px|link=&amp;#039;, link or &amp;#039;&amp;#039;, &amp;#039;|alt=&amp;#039;, altText, &amp;#039;|&amp;#039;, escapedTitle, &amp;#039;]]&amp;#039; )&lt;br /&gt;
	&lt;br /&gt;
	-- Add the stack number, if present and in 2-999 range&lt;br /&gt;
	if num and num &amp;gt; 1 and num &amp;lt; 1000 then&lt;br /&gt;
		if link then&lt;br /&gt;
			item:wikitext( &amp;#039;[[&amp;#039;, link, &amp;#039;|&amp;#039; )&lt;br /&gt;
		end&lt;br /&gt;
		local number = item&lt;br /&gt;
			:tag( &amp;#039;span&amp;#039; )&lt;br /&gt;
				:addClass( &amp;#039;invslot-stacksize&amp;#039; )&lt;br /&gt;
				:attr{ title = plainTitle }&lt;br /&gt;
				:wikitext( num )&lt;br /&gt;
		if args.numstyle then&lt;br /&gt;
			number:cssText( args.numstyle )&lt;br /&gt;
		end&lt;br /&gt;
		if link then&lt;br /&gt;
			item:wikitext( &amp;#039;]]&amp;#039; )&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- The HTML node is now ready&lt;br /&gt;
	return item&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Publicly available functions --&lt;br /&gt;
&lt;br /&gt;
-- Main entry point: Creates the whole slot&lt;br /&gt;
function p.slot( f )&lt;br /&gt;
	-- Incoming arguments&lt;br /&gt;
	local args = f.args or f&lt;br /&gt;
	if f == mw.getCurrentFrame() and args[1] == nil then&lt;br /&gt;
		args = f:getParent().args&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- TODO: Add support for unexpanded frame sequences in table format&lt;br /&gt;
	if not args.parsed then&lt;br /&gt;
		-- Assumed to be a string, trim it&lt;br /&gt;
		args[1] = mw.text.trim( args[1] or &amp;#039;&amp;#039; )&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Legacy mod support. Comment out instead of deleting; might be useful&lt;br /&gt;
	-- for other wikis&lt;br /&gt;
	-- TODO: Support multiple mod alias tables at once (like on RuMCW)&lt;br /&gt;
	local modData = {&lt;br /&gt;
		aliases = args.modaliases or &amp;#039;&amp;#039;,&lt;br /&gt;
		default = args.mod&lt;br /&gt;
	}&lt;br /&gt;
	if modData.aliases ~= &amp;#039;&amp;#039; then&lt;br /&gt;
		modData.aliases = mw.loadData( &amp;#039;Module:&amp;#039; .. modData.aliases )&lt;br /&gt;
	else&lt;br /&gt;
		modData.aliases = nil&lt;br /&gt;
	end&lt;br /&gt;
	if args.mod == &amp;#039;&amp;#039; then&lt;br /&gt;
		modData.default = nil&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Get the frame sequence in table format&lt;br /&gt;
	local frames&lt;br /&gt;
	if args.parsed then&lt;br /&gt;
		-- Already parsed in some other module, such as Recipe table&lt;br /&gt;
		frames = args[1]&lt;br /&gt;
	elseif args[1] ~= &amp;#039;&amp;#039; then&lt;br /&gt;
		-- Parse the frame string&lt;br /&gt;
		-- TODO: Make the “randomise” flag not hard-coded to invslot-large CSS class&lt;br /&gt;
		-- (ostensibly for output slots) as not all output slots are large&lt;br /&gt;
		local randomise = args.class == &amp;#039;invslot-large&amp;#039; and &amp;#039;never&amp;#039; or nil&lt;br /&gt;
		frames = p.parseFrameText( args[1], randomise, false, modData )&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Create the slot node and add applicable styles&lt;br /&gt;
	local body = mw.html.create( &amp;#039;span&amp;#039; ):addClass( &amp;#039;invslot&amp;#039; ):css{ [&amp;#039;vertical-align&amp;#039;] = args.align }&lt;br /&gt;
	&lt;br /&gt;
	-- Is the slot animated?&lt;br /&gt;
	local animated = frames and #frames &amp;gt; 1&lt;br /&gt;
	if animated then&lt;br /&gt;
		body:addClass( &amp;#039;animated&amp;#039; )&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Default background&lt;br /&gt;
	if ( args.default or &amp;#039;&amp;#039; ) ~= &amp;#039;&amp;#039; then -- default background&lt;br /&gt;
		body:addClass( &amp;#039;invslot-default-&amp;#039; .. string.lower( args.default ):gsub( &amp;#039; &amp;#039;, &amp;#039;-&amp;#039; ) )&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Custom styles&lt;br /&gt;
	body:addClass( args.class )&lt;br /&gt;
	body:cssText( args.style )&lt;br /&gt;
	&lt;br /&gt;
	--mw.logObject( frames )&lt;br /&gt;
	if not frames or #frames == 0 then&lt;br /&gt;
		-- Empty slot&lt;br /&gt;
		return tostring( body )&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- We have frames, add them&lt;br /&gt;
	local activeFrame = frames.randomise == true and random( #frames ) or 1&lt;br /&gt;
	for i, frame in ipairs( frames ) do&lt;br /&gt;
		local item&lt;br /&gt;
		if frame[1] then&lt;br /&gt;
			-- This is a subframe container. Each animation cycle of the slot&lt;br /&gt;
			-- will show a subframe, one at a time.&lt;br /&gt;
			-- Create a container node for subframes&lt;br /&gt;
			item = body:tag( &amp;#039;span&amp;#039; ):addClass( &amp;#039;animated-subframe&amp;#039; )&lt;br /&gt;
			local subActiveFrame = frame.randomise == true and random( #frame ) or 1&lt;br /&gt;
			&lt;br /&gt;
			-- Add subframes to the note&lt;br /&gt;
			for sI, sFrame in ipairs( frame ) do&lt;br /&gt;
				local sItem = makeItem( sFrame, args )&lt;br /&gt;
				item:node( sItem )&lt;br /&gt;
				&lt;br /&gt;
				-- Set this subframe as active&lt;br /&gt;
				if sI == subActiveFrame then&lt;br /&gt;
					sItem:addClass( &amp;#039;animated-active&amp;#039; )&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		else&lt;br /&gt;
			-- A simple frame&lt;br /&gt;
			item = makeItem( frame, args )&lt;br /&gt;
			body:node( item )&lt;br /&gt;
		end&lt;br /&gt;
		if i == activeFrame and animated then&lt;br /&gt;
			-- Set this frame as active, if we have multiple of them&lt;br /&gt;
			item:addClass( &amp;#039;animated-active&amp;#039; )&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- The slot is ready&lt;br /&gt;
	return tostring( body )&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Parses the frame text into a table of frames and subframes,&lt;br /&gt;
-- expanding aliases (and optionally retaining a reference), and&lt;br /&gt;
-- deciding if the slot can be randomised.&lt;br /&gt;
-- Alias references are used in [[Module:Recipe table]] to create links and&lt;br /&gt;
-- lists of unique items.&lt;br /&gt;
function p.parseFrameText( framesText, randomise, aliasReference, modData )&lt;br /&gt;
	-- Frame sequences&lt;br /&gt;
	local frames = { randomise = randomise }&lt;br /&gt;
	local subframes = {}&lt;br /&gt;
	&lt;br /&gt;
	-- Is the current frame a subframe?&lt;br /&gt;
	local subframe&lt;br /&gt;
	&lt;br /&gt;
	-- The list of expanded aliases, will be added to the frame sequence&lt;br /&gt;
	-- if aliasReference is set to true AND if there are any aliases to expand.&lt;br /&gt;
	local expandedAliases&lt;br /&gt;
	&lt;br /&gt;
	-- Split the frame string by semicolons (respecting square brackets)&lt;br /&gt;
	local splitFrames = splitOnUnenclosedSemicolons( framesText )&lt;br /&gt;
	&lt;br /&gt;
	-- Iterate on frame fragments&lt;br /&gt;
	for i, frameText in ipairs( splitFrames ) do&lt;br /&gt;
		-- Subframes are grouped by curly braces&lt;br /&gt;
		frameText = frameText:gsub( &amp;#039;^%s*{%s*&amp;#039;, function()&lt;br /&gt;
			subframe = true&lt;br /&gt;
			return &amp;#039;&amp;#039;&lt;br /&gt;
		end )&lt;br /&gt;
		&lt;br /&gt;
		if subframe then&lt;br /&gt;
			-- Closing brace found&lt;br /&gt;
			frameText = frameText:gsub( &amp;#039;%s*}%s*$&amp;#039;, function()&lt;br /&gt;
				subframe = &amp;#039;last&amp;#039;&lt;br /&gt;
				return &amp;#039;&amp;#039;&lt;br /&gt;
			end )&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		-- Convert the frame text into table format, applying the default mod&lt;br /&gt;
		-- if needed.&lt;br /&gt;
		local frame = p.makeFrame( frameText, modData and modData.default )&lt;br /&gt;
		&lt;br /&gt;
		-- Alias processing&lt;br /&gt;
		-- TODO: Rework mod support to automatically load relevant alias tables,&lt;br /&gt;
		-- for use on other wikis that may want it. This will allow supporting&lt;br /&gt;
		-- multiple mod alias tables at once. Comment out instead of deleting!&lt;br /&gt;
		local newFrame = frame&lt;br /&gt;
		if aliases or modData.aliases then&lt;br /&gt;
			local id = frame.name&lt;br /&gt;
			if frame.mod then&lt;br /&gt;
				-- is this really needed? RuMCW doesn’t add mod prefixes in mod aliases&lt;br /&gt;
				id = frame.mod .. &amp;#039;:&amp;#039; .. id&lt;br /&gt;
			end&lt;br /&gt;
			&lt;br /&gt;
			local alias = modData and modData.aliases and modData.aliases[id] or&lt;br /&gt;
				aliases and aliases[id]&lt;br /&gt;
			if alias then&lt;br /&gt;
				-- Alias found, expand it&lt;br /&gt;
				newFrame = p.getAlias( alias, frame )&lt;br /&gt;
				&lt;br /&gt;
				-- Save the alias references, if asked&lt;br /&gt;
				if aliasReference then&lt;br /&gt;
					-- The alias data includes the original unexpanded frame&lt;br /&gt;
					-- and the number of frames it has expanded to.&lt;br /&gt;
					-- The alias reference table is not sequential — indices for&lt;br /&gt;
					-- each alias data object correspond to that alias’ first&lt;br /&gt;
					-- (or only) expanded frame. Which is not added to the frame&lt;br /&gt;
					-- sequence yet&lt;br /&gt;
					local curFrame = #frames + 1&lt;br /&gt;
					local aliasData = { frame = frame, length = #newFrame }&lt;br /&gt;
					if subframe then&lt;br /&gt;
						-- Subframe containers will have their own&lt;br /&gt;
						-- alias reference tables&lt;br /&gt;
						if not subframes.aliasReference then&lt;br /&gt;
							subframes.aliasReference = {}&lt;br /&gt;
						end&lt;br /&gt;
						subframes.aliasReference[#subframes + 1] = aliasData&lt;br /&gt;
					else&lt;br /&gt;
						if not expandedAliases then&lt;br /&gt;
							expandedAliases = {}&lt;br /&gt;
						end&lt;br /&gt;
						expandedAliases[curFrame] = aliasData&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		-- Alias processing ends here&lt;br /&gt;
		&lt;br /&gt;
		-- Add frames and control randomization&lt;br /&gt;
		if subframe then&lt;br /&gt;
			-- Add the frame to the current subframe container&lt;br /&gt;
			mergeList( subframes, newFrame )&lt;br /&gt;
			&lt;br /&gt;
			-- Randomise starting frame for &amp;quot;Any *&amp;quot; aliases, as long as the&lt;br /&gt;
			-- alias is the only subframe (and randomization is not disabled)&lt;br /&gt;
			if frames.randomise ~= &amp;#039;never&amp;#039; and subframes.randomise == nil and&lt;br /&gt;
				frame.name:match( &amp;#039;^&amp;#039; .. i18n.prefixes.any .. &amp;#039; &amp;#039; )&lt;br /&gt;
			then&lt;br /&gt;
				subframes.randomise = true&lt;br /&gt;
			else&lt;br /&gt;
				subframes.randomise = false&lt;br /&gt;
			end&lt;br /&gt;
			&lt;br /&gt;
			-- Disable randomization&lt;br /&gt;
			if frames.randomise ~= &amp;#039;never&amp;#039; then&lt;br /&gt;
				frames.randomise = false&lt;br /&gt;
			end&lt;br /&gt;
			if subframe == &amp;#039;last&amp;#039; then&lt;br /&gt;
				if #subframes == 1 or #splitFrames == i and #frames == 0 then&lt;br /&gt;
					-- If the subframe container only has one expanded frame or&lt;br /&gt;
					-- is the only frame in the whole sequence, its contents are&lt;br /&gt;
					-- extracted into the main frame sequence&lt;br /&gt;
					local lastFrame = #frames&lt;br /&gt;
					mergeList( frames, subframes )&lt;br /&gt;
					&lt;br /&gt;
					-- Inherit the randomise flag if it’s the only frame&lt;br /&gt;
					if #splitFrames == 1 then&lt;br /&gt;
						frames.randomise = subframes.randomise&lt;br /&gt;
					end&lt;br /&gt;
					&lt;br /&gt;
					-- Append alias reference data, if present&lt;br /&gt;
					if aliasReference and subframes.aliasReference then&lt;br /&gt;
						if not expandedAliases then&lt;br /&gt;
							expandedAliases = {}&lt;br /&gt;
						end&lt;br /&gt;
						for i, aliasRefData in pairs(subframes.aliasReference) do&lt;br /&gt;
							expandedAliases[lastFrame + i] = aliasRefData&lt;br /&gt;
						end&lt;br /&gt;
					end&lt;br /&gt;
				else&lt;br /&gt;
					-- Add the subframe container to the frame sequence&lt;br /&gt;
					table.insert( frames, subframes )&lt;br /&gt;
				end&lt;br /&gt;
				&lt;br /&gt;
				-- Finished processing this subframe container&lt;br /&gt;
				subframes = {}&lt;br /&gt;
				subframe = nil&lt;br /&gt;
			end&lt;br /&gt;
		else&lt;br /&gt;
			-- Randomize starting frame for &amp;quot;Any *&amp;quot; aliases, as long as the alias is the only frame&lt;br /&gt;
			if frames.randomise ~= &amp;#039;never&amp;#039; and frame.name:match( &amp;#039;^&amp;#039; .. i18n.prefixes.any .. &amp;#039; &amp;#039; ) then&lt;br /&gt;
				frames.randomise = true&lt;br /&gt;
			else&lt;br /&gt;
				frames.randomise = false&lt;br /&gt;
			end&lt;br /&gt;
			&lt;br /&gt;
			-- Add the expanded frame(s) to the frame sequence&lt;br /&gt;
			mergeList( frames, newFrame )&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Add the alias reference, if we’re compiling one&lt;br /&gt;
	frames.aliasReference = expandedAliases&lt;br /&gt;
	&lt;br /&gt;
	-- The frame sequence is ready&lt;br /&gt;
	return frames&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Applies parameters from the parent frame (such as title or text)&lt;br /&gt;
-- to the alias’ expansion&lt;br /&gt;
function p.getAlias( aliasFrames, parentFrame )&lt;br /&gt;
	-- If alias is just a name, return the parent frame with the new name&lt;br /&gt;
	if type( aliasFrames ) == &amp;#039;string&amp;#039; then&lt;br /&gt;
		local expandedFrame = mw.clone( parentFrame )&lt;br /&gt;
		expandedFrame.name = aliasFrames&lt;br /&gt;
		return { expandedFrame }&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Single frame alias, put in list&lt;br /&gt;
	if aliasFrames.name then&lt;br /&gt;
		aliasFrames = { aliasFrames }&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Common case: group alias&lt;br /&gt;
	local expandedFrames = {}&lt;br /&gt;
	for i, aliasFrame in ipairs( aliasFrames ) do&lt;br /&gt;
		local expandedFrame&lt;br /&gt;
		if type( aliasFrame ) == &amp;#039;string&amp;#039; then&lt;br /&gt;
			-- Simple expansion frame in string format&lt;br /&gt;
			expandedFrame = { name = aliasFrame }&lt;br /&gt;
		else&lt;br /&gt;
			-- Expansion frame in table format&lt;br /&gt;
			-- As it’s loaded with mw.loadData, it must be cloned&lt;br /&gt;
			-- before changing&lt;br /&gt;
			expandedFrame = cloneTable( aliasFrame )&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		-- Apply the parent frame’s settings&lt;br /&gt;
		expandedFrame.title = parentFrame.title or expandedFrame.title&lt;br /&gt;
		expandedFrame.num = parentFrame.num or expandedFrame.num&lt;br /&gt;
		expandedFrame.text = parentFrame.text or expandedFrame.text&lt;br /&gt;
		&lt;br /&gt;
		-- Legacy mod support. Comment out instead of deleting&lt;br /&gt;
		-- TODO: invert the priority for mod parameter, to allow&lt;br /&gt;
		-- group mod aliases with vanilla items?&lt;br /&gt;
		expandedFrame.mod = parentFrame.mod or expandedFrame.mod&lt;br /&gt;
		&lt;br /&gt;
		expandedFrames[i] = expandedFrame&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return expandedFrames&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Convert the frame object back into string format&lt;br /&gt;
function p.stringifyFrame( frame )&lt;br /&gt;
	if not frame.name then&lt;br /&gt;
		return &amp;#039;&amp;#039;&lt;br /&gt;
	end&lt;br /&gt;
	return string.format(&lt;br /&gt;
		&amp;#039;[%s]%s:%s,%s[%s]&amp;#039;,&lt;br /&gt;
		frame.title or &amp;#039;&amp;#039;,&lt;br /&gt;
		frame.mod or &amp;#039;Minecraft&amp;#039;,&lt;br /&gt;
		frame.name,&lt;br /&gt;
		frame.num or &amp;#039;&amp;#039;,&lt;br /&gt;
		frame.text or &amp;#039;&amp;#039;&lt;br /&gt;
	)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Convert the frame sequence into string format&lt;br /&gt;
function p.stringifyFrames( frames )&lt;br /&gt;
	for i, frame in ipairs( frames ) do&lt;br /&gt;
		if frame[1] then&lt;br /&gt;
			-- Subframe container&lt;br /&gt;
			-- As the format and the syntax are the same, process it recursively&lt;br /&gt;
			frames[i] = &amp;#039;{&amp;#039; .. p.stringifyFrames( frame ) .. &amp;#039;}&amp;#039;&lt;br /&gt;
		else&lt;br /&gt;
			frames[i] = p.stringifyFrame( frame )&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return table.concat( frames, &amp;#039;;&amp;#039; )&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Converts the frame text into a frame object&lt;br /&gt;
-- Full syntax: [Title]Mod:Name,Number[Text]&lt;br /&gt;
function p.makeFrame( frameText, defaultMod )&lt;br /&gt;
	-- Simple frame with no parts&lt;br /&gt;
	if not frameText:match( &amp;#039;[%[:,]&amp;#039; ) then&lt;br /&gt;
		return {&lt;br /&gt;
			mod = defaultMod,&lt;br /&gt;
			name = mw.text.trim( frameText ),&lt;br /&gt;
		}&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Complex frame&lt;br /&gt;
	local frame = {}&lt;br /&gt;
	&lt;br /&gt;
	-- Title&lt;br /&gt;
	local title, rest = frameText:match( &amp;#039;^%s*%[([^%]]*)%]%s*(.*)&amp;#039; )&lt;br /&gt;
	if title then&lt;br /&gt;
		frame.title = title&lt;br /&gt;
		frameText = rest&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Additional tooltip text&lt;br /&gt;
	local rest, text = frameText:match( &amp;#039;([^%]]*)%s*%[([^%]]*)%]%s*$&amp;#039; )&lt;br /&gt;
	if text then&lt;br /&gt;
		frame.text = text&lt;br /&gt;
		frameText = rest&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Legacy mod support&lt;br /&gt;
	-- Comment out instead of deleting&lt;br /&gt;
	local mod, rest = frameText:match(&amp;#039;^([^:]+):%s*(.*)&amp;#039;)&lt;br /&gt;
	if mod then&lt;br /&gt;
		if not vanilla[mod:lower()] then&lt;br /&gt;
			frame.mod = mod&lt;br /&gt;
		end&lt;br /&gt;
		frameText = rest&lt;br /&gt;
	else&lt;br /&gt;
		frame.mod = defaultMod&lt;br /&gt;
		frameText = frameText:gsub(&amp;#039;^:&amp;#039;, &amp;#039;&amp;#039;)&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Name and stack size&lt;br /&gt;
	-- The pattern will match the last comma, so you can use names with commas&lt;br /&gt;
	-- like so: “Potatiesh, Greatstaff of the Peasant,1”&lt;br /&gt;
	local name, num = frameText:match(&amp;#039;(.*),%s*(%d+)&amp;#039;)&lt;br /&gt;
	if num then&lt;br /&gt;
		-- Number is set&lt;br /&gt;
		frame.name = mw.text.trim(name)&lt;br /&gt;
		frame.num = math.floor(num)&lt;br /&gt;
		if frame.num &amp;lt; 2 then&lt;br /&gt;
			frame.num = nil&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		-- No number&lt;br /&gt;
		frame.name = mw.text.trim(frameText)&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- The frame object is ready&lt;br /&gt;
	return frame&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- This line should be the last one:&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Ayutac</name></author>
	</entry>
</feed>