<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.boxofrocks.net/index.php?action=history&amp;feed=atom&amp;title=Module%3ACrafting_usage</id>
	<title>Module:Crafting usage - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.boxofrocks.net/index.php?action=history&amp;feed=atom&amp;title=Module%3ACrafting_usage"/>
	<link rel="alternate" type="text/html" href="https://wiki.boxofrocks.net/index.php?title=Module:Crafting_usage&amp;action=history"/>
	<updated>2026-05-31T00:07:53Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.34.2</generator>
	<entry>
		<id>https://wiki.boxofrocks.net/index.php?title=Module:Crafting_usage&amp;diff=575&amp;oldid=prev</id>
		<title>BoxWiki: 1 revision imported</title>
		<link rel="alternate" type="text/html" href="https://wiki.boxofrocks.net/index.php?title=Module:Crafting_usage&amp;diff=575&amp;oldid=prev"/>
		<updated>2021-05-13T18:14:04Z</updated>

		<summary type="html">&lt;p&gt;1 revision imported&lt;/p&gt;
&lt;table class=&quot;diff diff-contentalign-left&quot; data-mw=&quot;interface&quot;&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;1&quot; style=&quot;background-color: #fff; color: #222; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;1&quot; style=&quot;background-color: #fff; color: #222; text-align: center;&quot;&gt;Revision as of 18:14, 13 May 2021&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&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;/table&gt;</summary>
		<author><name>BoxWiki</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.boxofrocks.net/index.php?title=Module:Crafting_usage&amp;diff=574&amp;oldid=prev</id>
		<title>Planks&gt;AttemptToCallNil: testing fix for the category appearing on user pages</title>
		<link rel="alternate" type="text/html" href="https://wiki.boxofrocks.net/index.php?title=Module:Crafting_usage&amp;diff=574&amp;oldid=prev"/>
		<updated>2021-04-28T17:43:06Z</updated>

		<summary type="html">&lt;p&gt;testing fix for the category appearing on user pages&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;
local i18n = {&lt;br /&gt;
	emptyCategory = 'Empty crafting usage',&lt;br /&gt;
	moduleCrafting = [[Module:Crafting]],&lt;br /&gt;
	moduleSlot = [[Module:Inventory slot]],&lt;br /&gt;
	moduleText = [[Module:Text]],&lt;br /&gt;
	queryCategory = 'Recipe using $1',&lt;br /&gt;
	templateCrafting = 'Crafting',&lt;br /&gt;
}&lt;br /&gt;
p.i18n = i18n&lt;br /&gt;
&lt;br /&gt;
local text = require( i18n.moduleText )&lt;br /&gt;
local slot = require( i18n.moduleSlot )&lt;br /&gt;
local crafting = require( i18n.moduleCrafting )&lt;br /&gt;
local argList = {&lt;br /&gt;
	'ignoreusage', 'upcoming', 'name', 'ingredients', 'arggroups',&lt;br /&gt;
	1, 2, 3, 4, 5, 6, 7, 8, 9,&lt;br /&gt;
	'A1', 'B1', 'C1', 'A2', 'B2', 'C2', 'A3', 'B3', 'C3',&lt;br /&gt;
	'Output', 'description', 'fixed', 'notfixed',&lt;br /&gt;
	'A1title', 'A1link', 'B1title', 'B1link', 'C1title', 'C1link',&lt;br /&gt;
	'A2title', 'A2link', 'B2title', 'B2link', 'C2title', 'C2link',&lt;br /&gt;
	'A3title', 'A3link', 'B3title', 'B3link', 'C3title', 'C3link',&lt;br /&gt;
	'Otitle', 'Olink',&lt;br /&gt;
	'%PAGE%',&lt;br /&gt;
}&lt;br /&gt;
local prefixes = slot.i18n.prefixes&lt;br /&gt;
&lt;br /&gt;
local function map(tbl, func)&lt;br /&gt;
	local newtbl = {}&lt;br /&gt;
    for i,v in pairs(tbl) do&lt;br /&gt;
        newtbl[i] = func(v)&lt;br /&gt;
    end&lt;br /&gt;
    return newtbl&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Flatten a nested array, only doing the numerically-indexed parts.&lt;br /&gt;
local function flatten(tbl)&lt;br /&gt;
	local newtbl = {}&lt;br /&gt;
	local function _flat(arr)&lt;br /&gt;
		for _, v in ipairs(arr) do&lt;br /&gt;
			if type(v) == &amp;quot;table&amp;quot; and v[1] then&lt;br /&gt;
				_flat(v)&lt;br /&gt;
			else&lt;br /&gt;
				table.insert(newtbl, v)&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	_flat(tbl)&lt;br /&gt;
	return newtbl&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--[[Escapes special characters in ingredient names, and returns the correct&lt;br /&gt;
	pattern depending on the match type&lt;br /&gt;
--]]&lt;br /&gt;
local function createIngredientPatterns( ingredients, matchTypes )&lt;br /&gt;
	local patternChars = {&lt;br /&gt;
		['^'] = '%^';&lt;br /&gt;
		['$'] = '%$';&lt;br /&gt;
		['('] = '%(';&lt;br /&gt;
		[')'] = '%)';&lt;br /&gt;
		['%'] = '%%';&lt;br /&gt;
		['.'] = '%.';&lt;br /&gt;
		['['] = '%[';&lt;br /&gt;
		[']'] = '%]';&lt;br /&gt;
		['*'] = '%*';&lt;br /&gt;
		['+'] = '%+';&lt;br /&gt;
		['-'] = '%-';&lt;br /&gt;
		['?'] = '%?';&lt;br /&gt;
		['\0'] = '%z';&lt;br /&gt;
	}&lt;br /&gt;
	local patterns = {}&lt;br /&gt;
	for i, ingredient in ipairs( ingredients ) do&lt;br /&gt;
		local escaped = ingredient:gsub( '([^%w])', patternChars )&lt;br /&gt;
		if not matchTypes then&lt;br /&gt;
			patterns[i] = '%z' .. escaped .. '%z'&lt;br /&gt;
		else&lt;br /&gt;
			local matchType = matchTypes[i] or matchTypes&lt;br /&gt;
			if matchType == 'start' then&lt;br /&gt;
				patterns[i] = '%z' .. escaped&lt;br /&gt;
			elseif matchType == 'end' then&lt;br /&gt;
				patterns[i] = escaped .. '%z'&lt;br /&gt;
			else&lt;br /&gt;
				patterns[i] = escaped&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return patterns&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--[[Extracts the anonymous pipe-delimited arguments from the&lt;br /&gt;
	DPL query into a table with the corresponding keys, skipping&lt;br /&gt;
	templates with `ignoreusage` set, and skipping duplicate templates&lt;br /&gt;
--]]&lt;br /&gt;
&lt;br /&gt;
local extractArgs&lt;br /&gt;
do&lt;br /&gt;
	local seen = {}&lt;br /&gt;
	extractArgs = function( template )&lt;br /&gt;
		-- Check for duplicate template or `ignoreusage` arg&lt;br /&gt;
		if seen[template] or not template:find( '^\n|' ) then&lt;br /&gt;
			return&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		seen[template] = true&lt;br /&gt;
		&lt;br /&gt;
		local tArgs = {}&lt;br /&gt;
		local i = 1&lt;br /&gt;
		for arg in text.gsplit( template, '\n|' ) do&lt;br /&gt;
			if arg ~= '' then&lt;br /&gt;
				tArgs[argList[i]] = arg&lt;br /&gt;
			end&lt;br /&gt;
			i = i + 1&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		tArgs.nocat = '1'&lt;br /&gt;
		&lt;br /&gt;
		return tArgs&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--[[Loops through the crafting args and parses them, with alias reference data&lt;br /&gt;
	&lt;br /&gt;
	Identical slots reuse the same table, to allow them to be compared like strings&lt;br /&gt;
--]]&lt;br /&gt;
local function parseCraftingArgs( cArgs )&lt;br /&gt;
	local parsedFrameText = {}&lt;br /&gt;
	local parsedCArgs = {}&lt;br /&gt;
	for arg, frameText in pairs( cArgs ) do&lt;br /&gt;
		if frameText then&lt;br /&gt;
			local randomise = arg == 'Output' and 'never' or nil&lt;br /&gt;
			local frames = not randomise and parsedFrameText[frameText]&lt;br /&gt;
			if not frames then&lt;br /&gt;
				frames = slot.parseFrameText( frameText, randomise, true )&lt;br /&gt;
				parsedFrameText[frameText] = frames&lt;br /&gt;
			end&lt;br /&gt;
			parsedCArgs[arg] = frames&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return parsedCArgs&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Loops through the wanted ingredients, and checks if the name contains it&lt;br /&gt;
-- REQUIREMENT: name starts and ends with the NUL (\0) character. Simplifies operation&lt;br /&gt;
-- for multiple names in the variable.&lt;br /&gt;
local function containsIngredients( name, ingredientPatterns )&lt;br /&gt;
	for _, ingredient in pairs( ingredientPatterns ) do&lt;br /&gt;
		if name:find( ingredient ) then&lt;br /&gt;
			return true&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--[[Loops through the crafting ingredients and find which parameters and&lt;br /&gt;
	frames contain the wanted ingredients&lt;br /&gt;
	&lt;br /&gt;
	Returns a table if any matches are found, the table contains tables of&lt;br /&gt;
	required frame numbers, or true if all of them are required&lt;br /&gt;
--]]&lt;br /&gt;
local function findRequiredFrameNums( parsedCArgs, ingredientPatterns )&lt;br /&gt;
	local requiredFrameNums = {}&lt;br /&gt;
	local hasRequiredFrames&lt;br /&gt;
	for arg, frames in pairs( parsedCArgs ) do&lt;br /&gt;
		if arg ~= 'Output' then&lt;br /&gt;
			local requiredFrames = {}&lt;br /&gt;
			local count = 0&lt;br /&gt;
			for i, frame in ipairs( frames ) do&lt;br /&gt;
				-- Guess what? If we only take the first we lose the subframes.&lt;br /&gt;
				-- And then 'Cobblestone or Blackstone' starts breaking.&lt;br /&gt;
				local tframe = frame[1] and flatten(frame) or { frame }&lt;br /&gt;
				local names = '\0' .. table.concat(map(tframe, function (fr) return type(fr) == 'table' and fr.name or '' end), '\0') .. '\0'&lt;br /&gt;
				if containsIngredients( names, ingredientPatterns ) then&lt;br /&gt;
					requiredFrames[i] = true&lt;br /&gt;
					count = count + 1&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			if count &amp;gt; 0 then&lt;br /&gt;
				if count == #frames then&lt;br /&gt;
					return true&lt;br /&gt;
				end&lt;br /&gt;
				&lt;br /&gt;
				hasRequiredFrames = true&lt;br /&gt;
				requiredFrames.count = count&lt;br /&gt;
				requiredFrameNums[arg] = requiredFrames&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return hasRequiredFrames and requiredFrameNums&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--[[Generates the argument groups, either using the template's specified&lt;br /&gt;
	groups, or automatically based on the number of frames in each slot&lt;br /&gt;
--]]&lt;br /&gt;
local function generateArgGroups( predefinedArgGroups, parsedCArgs )&lt;br /&gt;
	local argGroups = {}&lt;br /&gt;
	if predefinedArgGroups or '' ~= '' then&lt;br /&gt;
		local i = 1&lt;br /&gt;
		for argGroup in text.gsplit( predefinedArgGroups, '%s*;%s*' ) do&lt;br /&gt;
			local groupData = { args = {} }&lt;br /&gt;
			for arg in text.gsplit( argGroup, '%s*,%s*' ) do&lt;br /&gt;
				arg = tonumber( arg ) or arg&lt;br /&gt;
				if not groupData.count then&lt;br /&gt;
					groupData.count = #parsedCArgs[arg]&lt;br /&gt;
				end&lt;br /&gt;
				groupData.args[arg] = true&lt;br /&gt;
			end&lt;br /&gt;
			argGroups[i] = groupData&lt;br /&gt;
			i = i + 1&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		for arg, frames in pairs( parsedCArgs ) do&lt;br /&gt;
			local framesLen = #frames&lt;br /&gt;
			if framesLen &amp;gt; 0 then&lt;br /&gt;
				local groupName = framesLen&lt;br /&gt;
				local alias = frames.aliasReference and frames.aliasReference[1]&lt;br /&gt;
				if alias and alias.length == framesLen and&lt;br /&gt;
					alias.frame.name:find( '^' .. prefixes.any .. ' ' )&lt;br /&gt;
				then&lt;br /&gt;
					groupName = alias.frame.name&lt;br /&gt;
				end&lt;br /&gt;
				&lt;br /&gt;
				local groupData = argGroups[groupName]&lt;br /&gt;
				if not groupData then&lt;br /&gt;
					groupData = {&lt;br /&gt;
						args = {},&lt;br /&gt;
						count = framesLen&lt;br /&gt;
					}&lt;br /&gt;
					argGroups[groupName] = groupData&lt;br /&gt;
				end&lt;br /&gt;
				groupData.args[arg] = true&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return argGroups&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--[[Adds together the required frames from each slot in this group&lt;br /&gt;
	to get the total amount of frames which are relevant&lt;br /&gt;
	&lt;br /&gt;
	Returns a table with the relevant frame numbers, if any are relevant&lt;br /&gt;
--]]&lt;br /&gt;
local function findRelevantFrameNums( requiredFrameNumData, group )&lt;br /&gt;
	local relevantFrameNums = {}&lt;br /&gt;
	local hasRelevantFrames&lt;br /&gt;
	for arg in pairs( group ) do&lt;br /&gt;
		local requiredFrameNums = requiredFrameNumData[arg]&lt;br /&gt;
		if requiredFrameNums and arg ~= 'Output' then&lt;br /&gt;
			for frameNum in pairs( requiredFrameNums ) do&lt;br /&gt;
				-- Have to use pairs as it contains a non-sequential set of numbers&lt;br /&gt;
				-- so we have to skip over the extra data in the table&lt;br /&gt;
				if frameNum ~= 'count' then&lt;br /&gt;
					hasRelevantFrames = true&lt;br /&gt;
					relevantFrameNums[frameNum] = true&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			&lt;br /&gt;
			relevantFrameNums.count = math.max(&lt;br /&gt;
				requiredFrameNums.count or 0,&lt;br /&gt;
				relevantFrameNums.count or 0&lt;br /&gt;
			)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return hasRelevantFrames and relevantFrameNums&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--[[Loops through the relevant frame numbers and extracts them&lt;br /&gt;
	into a new table, taking care of moving any alias references&lt;br /&gt;
	and cleaning up any unnecessary subframes&lt;br /&gt;
--]]&lt;br /&gt;
function p.extractRelevantFrames( relevantFrameNums, frames )&lt;br /&gt;
	local relevantFrames = { randomise = frames.randomise }&lt;br /&gt;
	local newFrameNum = 1&lt;br /&gt;
	for frameNum, frame in ipairs( frames ) do&lt;br /&gt;
		local relevantFrame = relevantFrameNums == true or relevantFrameNums[frameNum]&lt;br /&gt;
		if relevantFrame then&lt;br /&gt;
			if not frame[1] then&lt;br /&gt;
				local alias = frames.aliasReference and frames.aliasReference[frameNum]&lt;br /&gt;
				local moveAlias = true&lt;br /&gt;
				if alias and relevantFrameNums ~= true then&lt;br /&gt;
					for i = frameNum, alias.length do&lt;br /&gt;
						if not relevantFrameNums[i] then&lt;br /&gt;
							moveAlias = false&lt;br /&gt;
							break&lt;br /&gt;
						end&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
				if alias and moveAlias then&lt;br /&gt;
					if not relevantFrames.aliasReference then&lt;br /&gt;
						relevantFrames.aliasReference = {}&lt;br /&gt;
					end&lt;br /&gt;
					relevantFrames.aliasReference[newFrameNum] = alias&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			&lt;br /&gt;
			relevantFrames[newFrameNum] = frame&lt;br /&gt;
			newFrameNum = newFrameNum + 1&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Move frames in subframe to main frames, if the subframe&lt;br /&gt;
	-- is the only frame&lt;br /&gt;
	if not relevantFrames[2] and relevantFrames[1][1] then&lt;br /&gt;
		relevantFrames = relevantFrames[1]&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return relevantFrames&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--[[Works out what data is relevant to the requested ingredients&lt;br /&gt;
	&lt;br /&gt;
	If the template contains any of the ingredients, returns it with any&lt;br /&gt;
	necessary modifications, and with the crafting arguments parsed&lt;br /&gt;
--]]&lt;br /&gt;
function p.processTemplate( tArgs, ingredientPatterns )&lt;br /&gt;
	local cArgs = {}&lt;br /&gt;
	for i, v in pairs( crafting.cArgVals ) do&lt;br /&gt;
		cArgs[i] = tArgs[i] or tArgs[v]&lt;br /&gt;
	end&lt;br /&gt;
	cArgs.Output = tArgs.Output&lt;br /&gt;
	local parsedCArgs = parseCraftingArgs( cArgs )&lt;br /&gt;
	&lt;br /&gt;
	local requiredFrameNumData = findRequiredFrameNums( parsedCArgs, ingredientPatterns )&lt;br /&gt;
	if not requiredFrameNumData then&lt;br /&gt;
		return&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local newCArgs&lt;br /&gt;
	local modified&lt;br /&gt;
	if requiredFrameNumData == true then&lt;br /&gt;
		newCArgs = parsedCArgs&lt;br /&gt;
	else&lt;br /&gt;
		local argGroups = generateArgGroups( tArgs.arggroups, parsedCArgs )&lt;br /&gt;
		newCArgs = {}&lt;br /&gt;
		for _, groupData in pairs( argGroups ) do&lt;br /&gt;
			local group = groupData.args&lt;br /&gt;
			local relevantFrameNums = findRelevantFrameNums( requiredFrameNumData, group )&lt;br /&gt;
			if not relevantFrameNums then&lt;br /&gt;
				for arg in pairs( group ) do&lt;br /&gt;
					newCArgs[arg] = parsedCArgs[arg]&lt;br /&gt;
				end&lt;br /&gt;
			else&lt;br /&gt;
				modified = true&lt;br /&gt;
				for arg in pairs( group ) do&lt;br /&gt;
					newCArgs[arg] = p.extractRelevantFrames( relevantFrameNums, parsedCArgs[arg] )&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Convert arguments back to shapeless format if they were originally&lt;br /&gt;
	if tArgs[1] then&lt;br /&gt;
		local i = 1&lt;br /&gt;
		for argNum = 1, 9 do&lt;br /&gt;
			tArgs[argNum] = nil&lt;br /&gt;
			local cArg = newCArgs[argNum]&lt;br /&gt;
			if cArg then&lt;br /&gt;
				tArgs[i] = cArg&lt;br /&gt;
				i = i + 1&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		for i, arg in pairs( crafting.cArgVals ) do&lt;br /&gt;
			tArgs[arg] = newCArgs[i]&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	tArgs.Output = newCArgs.Output&lt;br /&gt;
	tArgs.parsed = true&lt;br /&gt;
	&lt;br /&gt;
	-- Let Module:Recipe table generate these&lt;br /&gt;
	-- with the modified crafting args&lt;br /&gt;
	if modified then&lt;br /&gt;
		tArgs.name = nil&lt;br /&gt;
		tArgs.ingredients = nil&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return tArgs&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--[[Works out which frame is the first frame, and returns its&lt;br /&gt;
	name, or alias name, for sorting purposes&lt;br /&gt;
--]]&lt;br /&gt;
function p.getFirstFrameName( frames, subframe )&lt;br /&gt;
	local frame = frames[1]&lt;br /&gt;
	if not subframe and frame[1] then&lt;br /&gt;
		return p.getFirstFrameName( frame[1], true )&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local alias = frames.aliasReference and frames.aliasReference[1]&lt;br /&gt;
	return alias and alias.frame.name or frame.name&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--[[Performs the DPL query which retrieves the arguments from the crafting&lt;br /&gt;
	templates on the pages within the requested categories.&lt;br /&gt;
	If more than four categories are given, break them down into batches of four.&lt;br /&gt;
--]]&lt;br /&gt;
local function dplQueryWrapper( category, ignore )&lt;br /&gt;
	local data = {}&lt;br /&gt;
	if type(category) == 'string' then&lt;br /&gt;
		category = mw.text.split(category, '|')&lt;br /&gt;
	else&lt;br /&gt;
		assert(type(category) == 'table')&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local includeStr = '{' .. i18n.templateCrafting .. '}:' .. table.concat( argList, ':' )&lt;br /&gt;
	local j = 1&lt;br /&gt;
	local count = #category&lt;br /&gt;
	local catParts = text.split( i18n.queryCategory, '%$1' )&lt;br /&gt;
	for i = 1, count, 4 do&lt;br /&gt;
		local catSlice = table.concat(category, '|', i, math.min(i + 3, count))&lt;br /&gt;
		data[j] = mw.getCurrentFrame():callParserFunction( '#dpl:', {&lt;br /&gt;
			category = catSlice,&lt;br /&gt;
			nottitleregexp = ignore,&lt;br /&gt;
			include = includeStr,&lt;br /&gt;
			mode = 'userformat',&lt;br /&gt;
			secseparators = '====',&lt;br /&gt;
			multisecseparators = '===='&lt;br /&gt;
		})&lt;br /&gt;
		j = j + 1&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return table.concat(data)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[The main body, which retrieves the data, and returns the relevant&lt;br /&gt;
	crafting templates, sorted alphabetically&lt;br /&gt;
--]]&lt;br /&gt;
function p.dpl( f )&lt;br /&gt;
	local args = f&lt;br /&gt;
	if f == mw.getCurrentFrame() then&lt;br /&gt;
		args = f:getParent().args&lt;br /&gt;
	else&lt;br /&gt;
		f = mw.getCurrentFrame()&lt;br /&gt;
	end&lt;br /&gt;
	local ingredients = args[1] and text.split( args[1], '%s*,%s*' ) or { mw.title.getCurrentTitle().text }&lt;br /&gt;
	local matchTypes = args.match and args.match:find( ',' ) and text.split( args.match, '%s*,%s*' ) or args.match&lt;br /&gt;
	local ingredientPatterns = createIngredientPatterns( ingredients, matchTypes )&lt;br /&gt;
	&lt;br /&gt;
	local data&lt;br /&gt;
	if args.category then&lt;br /&gt;
		data = dplQueryWrapper( args.category, args.ignore )&lt;br /&gt;
	else&lt;br /&gt;
		-- Need to format the catparts&lt;br /&gt;
		local catParts = text.split( i18n.queryCategory, '%$1' )&lt;br /&gt;
		local cats = map(ingredients, function (s)&lt;br /&gt;
			return catParts[1] .. s .. catParts[2]&lt;br /&gt;
		end)&lt;br /&gt;
		data = dplQueryWrapper( cats, args.ignore )&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local showDescription&lt;br /&gt;
	local templates = {}&lt;br /&gt;
	local i = 1&lt;br /&gt;
	for templateArgs in text.gsplit( data:sub( 5 ), '====' ) do&lt;br /&gt;
		local tArgs = extractArgs( templateArgs )&lt;br /&gt;
		local newArgs = tArgs and p.processTemplate( tArgs, ingredientPatterns )&lt;br /&gt;
		if newArgs then&lt;br /&gt;
			if tArgs.description then&lt;br /&gt;
				showDescription = '1'&lt;br /&gt;
			end&lt;br /&gt;
			&lt;br /&gt;
			templates[i] = {&lt;br /&gt;
				args = newArgs,&lt;br /&gt;
				sortKey = mw.ustring.lower(&lt;br /&gt;
					( newArgs.name or p.getFirstFrameName( newArgs.Output ) )&lt;br /&gt;
						:gsub( '^' .. prefixes.any .. ' ', '' )&lt;br /&gt;
						:gsub( '^' .. prefixes.matching .. ' ', '' )&lt;br /&gt;
						:gsub( '^%[%[', '' )&lt;br /&gt;
						:gsub( '^[^|]+|', '' )&lt;br /&gt;
				),&lt;br /&gt;
			}&lt;br /&gt;
			i = i + 1&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local templateCount = #templates&lt;br /&gt;
	if templateCount == 0 then&lt;br /&gt;
		if f:getTitle().isContentPage then&lt;br /&gt;
			return f:expandTemplate{ title = 'Translation category', args = { i18n.emptyCategory, project = '0' } }&lt;br /&gt;
		end&lt;br /&gt;
		return ''&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	table.sort( templates, function( a, b )&lt;br /&gt;
		return a.sortKey &amp;lt; b.sortKey&lt;br /&gt;
	end )&lt;br /&gt;
	&lt;br /&gt;
	local initialArgs = templates[1].args&lt;br /&gt;
	initialArgs.head = '1'&lt;br /&gt;
	initialArgs.showname = '1'&lt;br /&gt;
	initialArgs.showdescription = showDescription&lt;br /&gt;
	if not args.continue then&lt;br /&gt;
		templates[templateCount].args.foot = '1'&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local out = {}&lt;br /&gt;
	for i, template in ipairs( templates ) do&lt;br /&gt;
		out[i] = ( '&amp;lt;!-- [[' .. template.args['%PAGE%'] .. ']] --&amp;gt;\n'&lt;br /&gt;
		           .. crafting.table( template.args ) )&lt;br /&gt;
	end&lt;br /&gt;
	return table.concat( out, '\n' )&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Planks&gt;AttemptToCallNil</name></author>
		
	</entry>
</feed>